示例今天在做一个功能应用的时候,需要动态地画线(示例图如下),因为各个节点是动态从服务端请求过来的,所以线的数量也是动态的,这个时候对于画布的高度就要能自适应,但是对于canvas本身要预先设置好高度,但是一开始的高度又不知道(因为数据还没请求过来),那怎么处理呢,想了一个办法,就是等数据请求过来之后,然后计算出画布应该设置的高度,最后再把canvas开放出显示。
一、模板代码
<!-- 线路区域 -->
<view class="footmark-line-wrap" id="footmarkLineWrap">
<!-- 线路画布 -->
<canvas
v-if="isShowCanvas"
canvas-id="lineBg"
class="canvas-wrap"
:style="{ height: lineBgHeight + 'rpx' }"
></canvas>
<!-- 线路画布 -->
<!-- 节点 -->
<line-node
class="line-node"
:style="{
left: item.left + 'px',
top: item.top + 'px'
}"
:id="getNodeIndex(index)"
v-for="(item, index) in nodeList"
:key="item.id"
:node-info="item"
:node-index="index"
:current-pos="currentPos"
:button-color="buttonColor"
@detail="showNodeDetailModal"
></line-node>
<!-- 节点 -->
</view>
<!-- 线路区域 -->
二、数据变量
data() {
return {
//画布对象
canvasContext: null,
//节点高度
nodeSize: {
width: 0,
height: 0
},
//画布高度
lineBgHeight: 0,
//线路区域的宽度
lineAreaWidth: 0,
//路线节点列表
nodeList: [],
//是否显示画布
isShowCanvas: false
}
},
三、方法
//画布背景高度
setLineBgHeight() {
//通过获取到的节点数据来估算出画布的高度
let nodeNum = this.nodeList.length
if (nodeNum <= 3) {
this.lineBgHeight = 800
} else {
this.lineBgHeight = 260 * nodeNum
}
},
//获取节点的宽
async getLineAreaWidth() {
return new Promise((resolve) => {
let query = uni.createSelectorQuery().in(this)
query.select('#footmarkLineWrap').boundingClientRect()
query.exec((res) => {
if (res && res[0]) {
resolve(res[0].width)
}
})
})
},
//获取节点索引
getNodeIndex(index) {
return 'lineNode' + index
},
//获取节点的宽高
async getLineNodeSize(index) {
return new Promise((resolve) => {
let domid = '#' + this.getNodeIndex(index)
let query = uni.createSelectorQuery().in(this)
query.select(domid).boundingClientRect()
query.exec((res) => {
if (res && res[0]) {
resolve({
width: res[0].width,
height: res[0].height
})
} else {
resolve(false)
}
})
})
},
//处理节点列表(每个节点动态计算出坐标)
handleNodeList() {
//初始节点坐标
let xLeftPos = 0
let xRightPos = this.lineAreaWidth - 103
let yPos = 0
let findPos = false
this.nodeList = this.nodeList.map((item, index) => {
if (item.user_done == 0 && !findPos) {
this.currentPos = index
findPos = true
}
//判断当前索引处于偶数还是基数
let isEven = index % 2 == 0
//节点信息
let itemInfo = {
...item,
left: isEven
? xLeftPos + this.$u.random(0, this.lineAreaWidth / 6)
: xRightPos - this.$u.random(0, this.lineAreaWidth / 5),
top: yPos
}
yPos += 130
return itemInfo
})
},
//绘制线路
async drawLine() {
if (this.nodeList.length) {
//获取节点的大小
let nodeSize = await this.getLineNodeSize(0)
if (!nodeSize) {
nodeSize = {
width: 99,
height: 105
}
}
this.canvasContext = uni.createCanvasContext('lineBg')
//开始绘制
this.canvasContext.beginPath()
this.canvasContext.setShadow(10, 10, 50, '#EF808B')
this.canvasContext.setLineCap('round')
this.canvasContext.setLineJoin('round')
this.canvasContext.setStrokeStyle('#EF9D73')
this.canvasContext.setLineWidth(12)
//节点列表
this.nodeList.map((item, index) => {
let xPos = item.left + nodeSize.width / 2
let yPos = item.top + nodeSize.height / 2
if (index == 0) {
this.canvasContext.moveTo(xPos, yPos)
} else {
//前一个节点
let prePoint = this.nodeList[index - 1]
//控制点
let cxPos = (item.left + prePoint.left + nodeSize.width) / 2
let cyPos = (item.top + prePoint.top + nodeSize.height) / 2
//随机出曲线控制点方向
let controldirection = this.$u.random(0, 1)
if (controldirection) {
cyPos -= this.$u.random(30, 80)
} else {
cyPos += this.$u.random(30, 80)
}
this.canvasContext.quadraticCurveTo(cxPos, cyPos, xPos, yPos)
}
})
this.canvasContext.stroke()
this.canvasContext.draw()
}
},
四、业务处理
async onLoad(args) {
//获取节点数据列表
await this.getNodeList()
//设置画布高度
this.setLineBgHeight()
//高度设置完成之后开启显示canvas
this.isShowCanvas = true
//设置线路区域的宽度
this.lineAreaWidth = await this.getLineAreaWidth()
//处理节点
this.handleNodeList()
//绘制线路
this.drawLine()
},
五、CSS样式
.footmark-line-wrap {
position: relative;
.canvas-wrap {
width: 100%;
}
.line-node {
position: absolute;
}
}
网友评论