复杂流程卡的移动响应式设计
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-radius
和 border-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 的解决方案。
此设计适用于在 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-radius
和 border-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 的解决方案。