开发“连线题”并没什么难点,这里主要分享的是:实现过程中的一点点布局设计上的小心思。想要的效果大体长这样(如下图所示):
![](https://img.haomeiwen.com/i6782937/679dfbe6fb928c3a.png)
乍一看似乎需要获取每个元素的位置信息、计算连线的端点坐标,似乎很繁琐,而且每次页面尺寸变化时,都得重新计算。其实,事情可以比想象中简单很多…
一、方案设计
1.1 将连线题分为上中下三个区块,其中:
1)上下区块均使用flex(justify-content: space-around)
2)中间区块为canvas,在style属性中将width和height设置为100%
.row-1 { justify-content: space-around; }
.row-2 {
height: 0; flex-grow: 1;
canvas { width: 100%; height: 100%; }
}
.row-3 { justify-content: space-around; }
![](https://img.haomeiwen.com/i6782937/e8008c33b0cfc885.png)
1.2 这样设置以后,有几点好处:
1)计算直线端点(元素中点)坐标就会变得很轻松,只需要排序百分比与canvas的内容宽度相乘即可。
let x1 = (上方元素下标 * 2 + 1) / (上方元素总数 * 2) * canvas.width
let x2 = (下方元素下标 * 2 + 1) / (下方元素总数 * 2) * canvas.width
// 连线上方端点坐标: (x1, 0)
// 连线下方端点坐标: (x2, canvas.height)
2)页面resize时无需重新计算,页面也不会乱。当然如果resize前后差异较大,可能连线粗细程度会不美观。
经测,一般不重新绘制也问题不大;如果要求高的话,可以在resize时重新绘制一下。(下图是第一张图在网页resize后的效果,线条经过拉伸变细了)
![](https://img.haomeiwen.com/i6782937/23240cb55c89a8a4.png)
3)如果你连canvas的尺寸也懒得初始化,也是可以的,只不过效果会差些(线条有点模糊,粗细不美观),Chrome中canvas默认内容尺寸是300*100,效果如下图所示(截图可能视觉效果不明显):
![](https://img.haomeiwen.com/i6782937/91346455ea3ba272.png)
二、代码实现
线条绘制的相关代码如下:
Html
<canvas ref="canvas" :width="cvsWidth" :height="cvsHeight"></canvas>
Js
// 动态调整canvas的内容尺寸(必要时,可在每次resize时重复调用)
initCanvas() {
if (!this.$refs.canvas) return
let cvsInfo = this.$refs.canvas.getBoundingClientRect()
this.cvsWidth = parseInt(cvsInfo.width)
this.cvsHeight = parseInt(cvsInfo.height)
},
// 绘制连线
drawLine() {
if (!this.$refs.canvas) return
let count = this.dataList.length
let ctx = this.$refs.canvas.getContext("2d");
let cvsWidth = this.$refs.canvas.width
let cvsHeight = this.$refs.canvas.height
ctx.clearRect(0, 0, cvsWidth, cvsHeight);
ctx.lineWidth = 4;
ctx.lineCap = 'round';
ctx.strokeStyle = '#FF9C0A'
for (let k in this.answerDict) {
let _i1 = parseInt(k)
let _i2 = this.answerDict[k]
let _i1_x = (_i1 * 2 + 1) / (count * 2) * cvsWidth
let _i2_x = (_i2 * 2 + 1) / (count * 2) * cvsWidth
ctx.beginPath()
ctx.moveTo(_i1_x, 0);
ctx.lineTo(_i2_x, cvsHeight);
ctx.stroke()
}
},
网友评论