复杂流程卡的移动响应式设计

Mobile responsive design for complex process card

此设计适用于在 Angular 上构建的网站。一直在使用 canvas 和 html 。 canvas 用于围绕 circle 构建虚线和实线箭头。 html用于设计圆

HTML :

<div class="process-card-container">
  
  <div [style.margin-left.px]="canvasDivMargin" class="myCanvasDiv">
    <canvas id="myCanvas" width="2000" height="600">
      Your browser does not support the HTML5 canvas tag.</canvas>
  </div>

  <div class="all-cards">
    <div class="card-container" *ngFor="let card of data.cards;">
      <div (click)='onCTAClick(card.callToActions)'
        [ngClass]="{'each-process-card-without-pointer': card.callToActions == undefined, 'each-process-card' : card.callToActions !== undefined }"
        [ngStyle]="card.bgStyle">
        <div class="each-card-content-container">
          <span class="each-card-num">{{card.cardNumber}}</span>
          <span class="each-card-title">{{card.cardTitle}}</span>
        </div>
      </div>
    </div>
  </div>

</div>

SASS

.process-card-container {
  margin-top: 50px;
  margin-bottom: 20px;
  text-align: center;
  
}


.myCanvasDiv{
    padding-left: 0;
    padding-right: 0;
    margin-right: auto;
    display: block;
}

.card-container {
  display: inline-block;
  margin-right: 16px;
  margin-left: 75px;
}

.all-cards {
  margin-left: -8rem;
  margin-top: -553px;
}

.each-process-card {
  height: 250px;
  width: 250px;
  border-radius: 50%;
  text-align: center;
  cursor: pointer;
}

.each-process-card-without-pointer {
  height: 250px;
  width: 250px;
  border-radius: 50%;
  text-align: center;
}

.each-card-content-container {
  position: relative;
}

.each-card-num {
  position: absolute;
    color: #ffffff;
    font-size: 54px;
    top: 30px;
    left: 110px;
    padding-left: 0;
    padding-right: 0;
    margin-left: 0;
    margin-right: 0;
    display: block;
}

.each-card-title {
  font-size: 22px;
    font-family: "Metropolis", "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-weight: bold;
    color: white;
    letter-spacing: 0.8px;
    width: 120px;
    position: absolute;
    top: 109px;
    left: 65px;
    height: 20px;
}

该设计在一个屏幕上运行良好,但在其他分辨率的屏幕上会出现问题,放大/缩小时,箭头和圆圈也到处乱跑。

TS文件

import { Component, Input, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-process-card',
  templateUrl: './process-card.component.html',
  styleUrls: ['./process-card.component.scss']
})
export class ProcessCardComponent implements OnInit {

  constructor(public router: Router) { }

  @Input()
  public data;

  public arrowColorArr = [];

  public dia = 170;
  public top_position = 180;
  public left_position = 180;
  public c;
  public ctx;

  public canvasDiv;
  public canvasDivMargin = 36;

 
  ngOnInit() {
    this.arrowColorArr = this.data.cards.map((cardIn) => {
      var eachBgColor = cardIn.bgStyle.background.includes("linear-gradient") ? cardIn.bgStyle.background.substring(35, 43) : cardIn.bgStyle.background;
      return eachBgColor.replace(/ /g, '')
    })
    
    this.c = document.getElementById('myCanvas');
    this.ctx = this.c.getContext("2d");

    this.ctx.setLineDash([0.5, 15]);
    this.ctx.lineWidth = 7;
    this.ctx.lineCap = "round";
    //1st down
    this.ctx.strokeStyle = this.arrowColorArr[1];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position, this.top_position + 4, this.dia, 0, 1 * Math.PI);
    this.ctx.stroke();

    //second circle up
    this.ctx.strokeStyle = "#0091da";
    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 3.1, 2 * Math.PI);
    this.ctx.stroke();

    //3rd circle down
    this.ctx.strokeStyle = this.arrowColorArr[3];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 0, 1 * Math.PI);
    this.ctx.stroke();


    //4th circle up
    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 3.15, 1.98 * Math.PI);
    this.ctx.stroke();

    this.ctx.lineWidth = 5;
    //1st up
    this.ctx.setLineDash([0, 0]);
    this.ctx.strokeStyle = this.arrowColorArr[0];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position, this.top_position, this.dia, 3.3, 1.8 * Math.PI);
    this.ctx.stroke();

    //2nd down
    this.ctx.strokeStyle = this.arrowColorArr[1];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 0.5, 0.9 * Math.PI);
    this.ctx.stroke();

    //3rd up
    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 3.5, 1.8 * Math.PI);
    this.ctx.stroke();

    //4th down
    this.ctx.strokeStyle = this.arrowColorArr[3];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 0.1, 0.9 * Math.PI);
    this.ctx.stroke()

    //arrow mark 
    this.ctx.strokeStyle = this.arrowColorArr[0];

    this.ctx.beginPath();
    this.ctx.moveTo(330, 92);
    this.ctx.lineTo(327, 79);
    this.ctx.lineTo(317, 89);
    this.ctx.closePath();
    this.ctx.stroke();

    this.ctx.strokeStyle = this.arrowColorArr[1];
    this.ctx.beginPath();
    this.ctx.moveTo(677, 248);
    this.ctx.lineTo(678, 262);
    this.ctx.lineTo(667, 256);
    this.ctx.closePath();
    this.ctx.stroke();

    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.moveTo(1011, 90);
    this.ctx.lineTo(1007, 77);
    this.ctx.lineTo(997, 87);
    this.ctx.closePath();
    this.ctx.stroke();

    this.ctx.strokeStyle = this.arrowColorArr[3];
    this.ctx.beginPath();
    this.ctx.moveTo(1370, 183);
    this.ctx.lineTo(1375, 193);
    this.ctx.lineTo(1365, 193);
    this.ctx.closePath();
    this.ctx.stroke();
  }
}

canvas 在视口尺寸 1920 x 1080 下绘制正常。

此代码片段(用于演示目的的 vanilla JS)绘制问题代码中给出的 canvas,然后缩放它及其位置以适应当前视口。

  let arrowColorArr = ["red", "green", "blue", "grey"];

  const dia = 170;
  const top_position = 180;
  const left_position = 180;

 
 function OnInit() {
    // for vanilla JS demo only we need to set up the values
    this.arrowColorArr = arrowColorArr; 
    this.dia = dia;
    this.top_position = top_position;
    this.left_position = left_position;
    this.c = document.getElementById('myCanvas');
    
    //ADDED - RESTORE CANVAS TO INITIAL SCALE
    c.style.transform = "scale(1)";
    
    this.ctx = this.c.getContext("2d");
    this.ctx.clearRect(0, 0, 1920, 600); //ADDED
    
    this.ctx.setLineDash([0.5, 15]);
    this.ctx.lineWidth = 7;
    this.ctx.lineCap = "round";
    //1st down
    this.ctx.strokeStyle = this.arrowColorArr[1];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position, this.top_position + 4, this.dia, 0, 1 * Math.PI);
    this.ctx.stroke();

    //second circle up
    this.ctx.strokeStyle = "#0091da";
    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 3.1, 2 * Math.PI);
    this.ctx.stroke();

    //3rd circle down
    this.ctx.strokeStyle = this.arrowColorArr[3];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 0, 1 * Math.PI);
    this.ctx.stroke();


    //4th circle up
    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 3.15, 1.98 * Math.PI);
    this.ctx.stroke();

    this.ctx.lineWidth = 5;
    //1st up
    this.ctx.setLineDash([0, 0]);
    this.ctx.strokeStyle = this.arrowColorArr[0];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position, this.top_position, this.dia, 3.3, 1.8 * Math.PI);
    this.ctx.stroke();

    //2nd down
    this.ctx.strokeStyle = this.arrowColorArr[1];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 0.5, 0.9 * Math.PI);
    this.ctx.stroke();

    //3rd up
    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 3.5, 1.8 * Math.PI);
    this.ctx.stroke();

    //4th down
    this.ctx.strokeStyle = this.arrowColorArr[3];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 0.1, 0.9 * Math.PI);
    this.ctx.stroke()

    //arrow mark 
    this.ctx.strokeStyle = this.arrowColorArr[0];

    this.ctx.beginPath();
    this.ctx.moveTo(330, 92);
    this.ctx.lineTo(327, 79);
    this.ctx.lineTo(317, 89);
    this.ctx.closePath();
    this.ctx.stroke();

    this.ctx.strokeStyle = this.arrowColorArr[1];
    this.ctx.beginPath();
    this.ctx.moveTo(677, 248);
    this.ctx.lineTo(678, 262);
    this.ctx.lineTo(667, 256);
    this.ctx.closePath();
    this.ctx.stroke();

    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.moveTo(1011, 90);
    this.ctx.lineTo(1007, 77);
    this.ctx.lineTo(997, 87);
    this.ctx.closePath();
    this.ctx.stroke();

    this.ctx.strokeStyle = this.arrowColorArr[3];
    this.ctx.beginPath();
    this.ctx.moveTo(1370, 183);
    this.ctx.lineTo(1375, 193);
    this.ctx.lineTo(1365, 193);
    this.ctx.closePath();
    this.ctx.stroke();
    //ADDED SO CANVAS SCALES TO WINDOW WIDTH
    const scale = window.innerWidth/1920;// 1920 because it draws the canvas OK at that viewport width
    c.style.transform = "scale(" + scale + ")";
    c.style.top = this.top_position * scale + "px";
    c.style.left = this.left_position * scale + "px";
  }
window.onload = OnInit;
window.onresize = OnInit;
* {
  margin: 0;
  padding: 0;
  }
canvas {
  position: relative;
  transform-origin: 0 0;
}
<canvas id="myCanvas" width="1500" height="600"></canvas><!-- was 2000 wide, reduced so doesn't cause X overflow when scaled -->

插入这些更改后,您的 canvas 绘图代码因此变为:

<canvas id="myCanvas" width="1500" height="600"></canvas><!-- was 2000 wide, reduced so doesn't cause X overflow when scaled -->

TS代码:

import { Component, Input, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-process-card',
  templateUrl: './process-card.component.html',
  styleUrls: ['./process-card.component.scss']
})
export class ProcessCardComponent implements OnInit {

  constructor(public router: Router) { }

  @Input()
  public data;

  public arrowColorArr = [];

  public dia = 170;
  public top_position = 180;
  public left_position = 180;
  public c;
  public ctx;

  public canvasDiv;
  public canvasDivMargin = 36;

 
  ngOnInit() {
    this.arrowColorArr = this.data.cards.map((cardIn) => {
      var eachBgColor = cardIn.bgStyle.background.includes("linear-gradient") ? cardIn.bgStyle.background.substring(35, 43) : cardIn.bgStyle.background;
      return eachBgColor.replace(/ /g, '')
    })
    
    this.c = document.getElementById('myCanvas');

    
    //ADDED - RESTORE CANVAS TO INITIAL SCALE
    c.style.transform = "scale(1)";

    this.ctx = this.c.getContext("2d");
    // ADDED TO CLEAR THE CANVAS EACH TIME (ON A RESIZE E.G.)
    this.ctx.clearRect(0, 0, 1500, 600);

    this.ctx.setLineDash([0.5, 15]);
    this.ctx.lineWidth = 7;
    this.ctx.lineCap = "round";
    //1st down
    this.ctx.strokeStyle = this.arrowColorArr[1];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position, this.top_position + 4, this.dia, 0, 1 * Math.PI);
    this.ctx.stroke();

    //second circle up
    this.ctx.strokeStyle = "#0091da";
    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 3.1, 2 * Math.PI);
    this.ctx.stroke();

    //3rd circle down
    this.ctx.strokeStyle = this.arrowColorArr[3];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 0, 1 * Math.PI);
    this.ctx.stroke();


    //4th circle up
    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 3.15, 1.98 * Math.PI);
    this.ctx.stroke();

    this.ctx.lineWidth = 5;
    //1st up
    this.ctx.setLineDash([0, 0]);
    this.ctx.strokeStyle = this.arrowColorArr[0];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position, this.top_position, this.dia, 3.3, 1.8 * Math.PI);
    this.ctx.stroke();

    //2nd down
    this.ctx.strokeStyle = this.arrowColorArr[1];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 0.5, 0.9 * Math.PI);
    this.ctx.stroke();

    //3rd up
    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 3.5, 1.8 * Math.PI);
    this.ctx.stroke();

    //4th down
    this.ctx.strokeStyle = this.arrowColorArr[3];
    this.ctx.beginPath();
    this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 0.1, 0.9 * Math.PI);
    this.ctx.stroke()

    //arrow mark 
    this.ctx.strokeStyle = this.arrowColorArr[0];

    this.ctx.beginPath();
    this.ctx.moveTo(330, 92);
    this.ctx.lineTo(327, 79);
    this.ctx.lineTo(317, 89);
    this.ctx.closePath();
    this.ctx.stroke();

    this.ctx.strokeStyle = this.arrowColorArr[1];
    this.ctx.beginPath();
    this.ctx.moveTo(677, 248);
    this.ctx.lineTo(678, 262);
    this.ctx.lineTo(667, 256);
    this.ctx.closePath();
    this.ctx.stroke();

    this.ctx.strokeStyle = this.arrowColorArr[2];
    this.ctx.beginPath();
    this.ctx.moveTo(1011, 90);
    this.ctx.lineTo(1007, 77);
    this.ctx.lineTo(997, 87);
    this.ctx.closePath();
    this.ctx.stroke();

    this.ctx.strokeStyle = this.arrowColorArr[3];
    this.ctx.beginPath();
    this.ctx.moveTo(1370, 183);
    this.ctx.lineTo(1375, 193);
    this.ctx.lineTo(1365, 193);
    this.ctx.closePath();
    this.ctx.stroke();


    //ADDED SO CANVAS SCALES TO WINDOW WIDTH
    const scale = window.innerWidth/1920;// 1920 because it draws the canvas OK at that viewport width
    c.style.transform = "scale(" + scale + ")";
    c.style.top = this.top_position * scale + "px";
    c.style.left = this.left_position * scale + "px";
  }
}

您同样需要缩放 HTML 元素的大小和位置。如果您需要这方面的帮助,请告诉我。

并记得在调整大小事件中添加重新计算 canvas 所需的任何内容。

只需简单的 HTML 和 CSS 就可以非常非常接近!我使用一个有序列表 border-radiusborder-style 以及一个用于指针箭头的微型 SVG 来完成此操作。这有几个优点:

##Edit - 请参阅下面的所有装饰元素都使用 SVG 的版本...

  • 更简单的代码
  • 使用浏览器的布局和渲染引擎
  • 语义正确 HTML 和实时文本
  • 自动步骤编号
  • 根据列表位置自动着色
  • 能够resize/respond按要求
  • 能够根据需要轻松 add/remove 步骤

缺点主要是您不能绝对精细地控制像素位置、间距和样式,例如点和线。我认为这是一个非常合理的优势权衡,但这取决于您的情况。对我来说,结果是这样的:

这是代码

.disc-list {
  counter-reset: step; /* Allows us to show the step number */
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: row; /* Put the discs in a row */
  flex-wrap: wrap; /* Allow them to wrap around as required */
}

.disc-list li {
  counter-increment: step; /* Increment the list number */
  position: relative;
  width: 8.5rem;
  height: 8.5rem;
  text-align: center;
  border-radius: 50%; /* Make it a circle */
  background: #888;
  color: #888;
  display: flex;
  flex-shrink: 0; /* Don't allow the discs to squash inside the container */
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin: 1.5rem 1.25rem 1.5rem 1.5rem; /* Right margin is the normal width - the border-width */
  overflow: visible;
}

/* Setup the rainbow colours on the discs 
  - #00AFC0
  - #00A3E1
  - #27579C
  - #934FC0
  - #C04F8E
  - #C04F5D
  - #C07E4F
  - #C0B54F
*/

.disc-list li:nth-child(1) {
  background: #00AFC0;
  color: #00AFC0;
}

.disc-list li:nth-child(2) {
  background: #00A3E1;
  color: #00A3E1;
}

.disc-list li:nth-child(3) {
  background: #27579C;
  color: #27579C;
}

.disc-list li:nth-child(4) {
  background: #934FC0;
  color: #934FC0;
}

.disc-list li:nth-child(5) {
  background: #C04F8E;
  color: #C04F8E;
}

.disc-list li:nth-child(6) {
  background: #C04F5D;
  color: #C04F5D;
}

.disc-list li:nth-child(7) {
  background: #C07E4F;
  color: #C07E4F;
}

.disc-list li:nth-child(8) {
  background: #C0B54F;
  color: #C0B54F;
}

/* The text inside the disc */
.disc-list li span {
  color: #FFF;
  display: block;
  font-weight: bold;
  font-size: 0.8rem;
  max-width: 5rem;
  margin-left: auto;
  margin-right: auto;
}

/* The step number */
.disc-list li span::before {
  content: counter(step);
  display: block;
  font-size: 1.65rem;
  margin-bottom: 0.1rem;
  font-weight: 200;
}

/* The pointer container */
.disc-list li .pointer {
  position: absolute;
  right: -1.6rem;
  width: 1rem;
  height: 1rem;
}

/* The arrow-head itself */
.disc-list li .pointer path {
 fill: none;
 stroke-linejoin: round;
 stroke-width: 3px;
 stroke: currentcolor; /* This allows the outline to inherit the text color automatically */
}

/* Move the arrow head into position depending if it's an odd/even disc */
.disc-list li:nth-child(even) .pointer {
  bottom: 20%;
  transform: rotate(22.5deg);
}

.disc-list li:nth-child(odd) .pointer {
  top: 20%;
  transform: rotate(22.5deg);
}

.disc-list li::before,
.disc-list li::after {
  content: '';  
  position: absolute;
  border: 0.25rem solid;
  left: -1.5rem; /* Same as the disc-list li margin */
  right: -1.5rem;
}

/* The dotted elements */
.disc-list li:nth-child(even)::before {
  border-top-style: dotted;
  border-left-style: dotted;
  border-radius: 50%/100% 100% 0 0;
  border-bottom-color: transparent;
  top: -1.5rem;
  bottom: 50%;
}

.disc-list li:nth-child(odd)::before {
  border-bottom-style: dotted;
  border-right-style: dotted;
  border-top-color: transparent;
  border-radius: 50%/0 0 100% 100%;
  bottom: -1.5rem;
  top: 50%;
}

/* The solid line elements */
.disc-list li:nth-child(odd)::after {
  transform-origin: bottom center;
  transform: rotate(22.5deg);
  border-width: 0.2rem;
  border-style: solid;
  border-color: transparent;
  border-top-color: inherit;
  border-left-color: inherit;
  border-radius: 50%/100% 100% 0 0;
  border-bottom-color: transparent;
  top: -1.5rem;
  bottom: 50%;
}

.disc-list li:nth-child(even)::after {
  transform-origin: top center;
  transform: rotate(22.5deg);
  border-style: solid;
  border-width: 0.2rem;
  border-color: transparent;
  border-bottom-color: inherit;
  border-right-color: inherit;
  border-radius: 50%/0 0 100% 100%;
  bottom: -1.5rem;
  top: 50%;
}
<ol class="disc-list">
  
  <li>
    <span>Login to Workday</span>
    <svg class="pointer">
      <path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z" />
    </svg>
  </li>
  <li>
    <span>Upload to Receipts</span>
    <svg class="pointer">
      <path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z" />
    </svg>
  </li>
  <li>
    <span>Submit Report</span>
    <svg class="pointer">
      <path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z" />
    </svg>
  </li>
  <li>
    <span>Get Reimbursed</span>
    <svg class="pointer">
      <path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z"/>
    </svg>
  </li> 
  <li>
    <span>Do a Dance</span>
    <svg class="pointer">
      <path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z"/>
    </svg>
  </li>
  <li>
    <span>Make Sandwiches</span>
    <svg class="pointer">
      <path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z"/>
    </svg>
  </li>
  <li>
    <span>Take a nap</span>
    <svg class="pointer">
      <path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z"/>
    </svg>
  </li>
  <li>
    <span>Go again!</span>
    <svg class="pointer">
      <path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z"/>
    </svg>
  </li>
  
</ol>

我相信您可以进一步微调,并优化 CSS 以使其更整洁 - 不过,这应该是一个不错的起点。如果需要,请随时询问更多详细信息!

SVG 版本

此版本使用内联 SVG 作为装饰元素,而不是边框​​样式。这应该会提供更一致的结果,并允许您微调破折号等内容。为了简洁起见,我用 SASS 重写了它,SO 的代码片段不支持,所以这是在 CodePen 上:https://codepen.io/companionstudio/pen/BaWqBmm

这还没有改变 wrapping/scaling 要求,但如果您需要包装,我会强烈推荐基于 HTML 的解决方案。