Files
smart_storage_app/components/new-canvas/index.vue
2025-12-26 11:26:40 +08:00

388 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view style="width: 100%;height: 100%;position: relative;">
<zero-loading v-show="loading && !noloading" position="absolute" v-if="loading && !noloading"></zero-loading>
<canvas :id="cId" type="2d" :style="{width:width,height:height}" :canvas-id="cId"></canvas>
</view>
</template>
<script>
export default {
data() {
return {
windowWidth: 0,
type: 'app',
ctx: null,
textArr: [],
imgArr: [],
lineArr: [],
circleArr: [],
rectArr: [],
loading: true,
canvasWidth: null,
canvasHeight: null
}
},
props: {
cId: {
type: [String, Number],
default: 'canvas'
},
width: {
type: [String, Number],
default: '100%'
},
height: {
type: [String, Number],
default: '100%'
},
canvasData: {
type: Array,
default: () => {
return []
}
},
noloading: {
type: Boolean,
default: false
}
},
watch: {
canvasData: {
handler(val) {
// 等待 windowWidth 初始化完成后再绘制
if (this.windowWidth) {
this.draw(null); // 直接绘制
} else {
// 若 windowWidth 未获取,先获取再绘制
this.getSystemInfo().then(() => {
this.draw(null);
});
}
},
deep: true,
immediate: false
}
},
mounted() {
// this.getSystemInfo()
},
methods: {
getSystemInfo() {
const self = this
uni.getSystemInfo({
success: function(res) {
// 获取屏幕尺寸
self.windowWidth = res.windowWidth
// const platform = res.hostName.toLowerCase()
if (res.uniPlatform || res.uniPlatform === 'app') {
self.type = 'app'
//获取app的canvas的dom
self.getAppDom()
} else {
self.type = 'wx'
//获取小程序的canvas的dom
self.getWXDom()
}
}
})
},
clear() {
// if (this.type === 'wx') {
// this.ctx.globalCompositeOperation = 'destination-out';
// this.ctx.beginPath();
// this.ctx.fillStyle = 'red';
// this.ctx.fillRect(0, 0, 8000, 80000);
// this.ctx.fill();
// this.ctx.globalCompositeOperation = 'source-over';
// // this.ctx.draw();
// } else {
// // this.ctx.clearRect(2000, 2000, 2000, 2000);
// // this.ctx.fillStyle = '#ffffff'
// // this.ctx.draw(true);
// }
if (this.type === 'wx') {
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
} else {
// App 平台
this.ctx.clearRect(0, 0, 8000, 8000);
}
},
//获取app的canvas DOM
getAppDom() {
var context = uni.createCanvasContext(`${this.cId}`)
this.ctx = context
this.draw(null)
},
//获取微信的canvas DOM
getWXDom() {
// const instance = getCurrentInstance()
let query = wx.createSelectorQuery().in(this)
query
.select(`#${this.cId}`)
.fields({
node: true,
size: true
}, () => {})
.exec(this.initCanvas.bind(this))
},
initCanvas(res) {
const width = res[0].width
const height = res[0].height
const canvas = res[0].node
this.ctx = canvas.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = width * dpr
canvas.height = height * dpr
this.canvasWidth = canvas.width
this.canvasHeight = canvas.height
this.ctx.scale(dpr, dpr)
this.draw(canvas)
},
draw(canvas) {
this.textArr = []
this.circleArr = []
this.rectArr = []
this.imgArr = []
this.lineArr = []
this.canvasData.forEach((item) => {
if (item.type === 'text') {
this.textArr.push(item)
}
if (item.type === 'image') {
this.imgArr.push(item)
}
if (item.type === 'line') {
this.lineArr.push(item)
}
if (item.type === 'circle') {
this.circleArr.push(item)
}
if (item.type === 'rect') {
this.rectArr.push(item)
}
})
if (this.imgArr.length) {
this.drawImages(this.imgArr, canvas ? canvas : null)
}
if (this.lineArr.length) {
this.drawLine(this.lineArr)
}
if (this.circleArr.length) {
this.drawCircle(this.circleArr)
}
if (this.rectArr.length) {
this.drawRect(this.rectArr)
}
if (this.textArr.length) {
this.drawText(this.textArr)
}
// this.drawLine(this.lineArr)
// this.drawCircle(this.circleArr)
// this.drawRect(this.rectArr)
// this.drawText(this.textArr)
if (this.type === 'app') {
this.ctx.draw()
}
this.loading = false
},
drawRect(draw) {
draw.forEach((item, index) => {
let x = this.fitSize(item.coord[0][0])
let y = this.fitSize(item.coord[0][1])
let w = this.fitSize(item.coord[1][0])
let h = this.fitSize(item.coord[1][1])
this.ctx.beginPath();
if (this.type === 'app') {
if (item.rectType === 'stroke') {
this.ctx.setStrokeStyle(item.borderColor); // 设置边框颜色
this.ctx.setLineWidth(item.width); // 设置边框宽度
this.ctx.strokeRect(x, y, w, h); // 绘制无填充矩形
} else {
this.ctx.setFillStyle(item.background || 'transparent');
this.ctx.fillRect(x, y, w, h);
}
} else {
if (item.rectType === 'stroke') {
this.ctx.strokeStyle = item.borderColor
this.ctx.lineWidth = item.width
this.ctx.strokeRect(x, y, w, h);
} else {
this.ctx.fillStyle = item.background || 'transparent'
this.ctx.fillRect(x, y, w, h);
}
}
})
},
// countChineseAndEnglishCharacters(str, x) {
// var chineseCount = str.match(/[\u4e00-\u9fa5]/g) ?
// str.match(/[\u4e00-\u9fa5]/g).length :
// 0;
// var englishCount = str.match(/[a-zA-Z]/g) ?
// str.match(/[a-zA-Z]/g).length :
// 0;
// var otherCount = str.length - chineseCount - englishCount;
// const obj = {
// otherCount: otherCount,
// chineseCount: chineseCount,
// englishCount: englishCount,
// };
// return (
// obj.englishCount * 6 + obj.chineseCount * 12 + obj.otherCount * 7.5 + x
// );
// },
drawText(draw) {
draw.forEach((item, index) => {
if (item.font.length) {
// 记录前一个文本的结束位置(用于后续文本偏移)
let prevTextEndX = this.fitSize(item.coord[0][0]);
item.font.forEach((item2, index2) => {
// 设置字体大小和颜色
if (this.type === 'app') {
this.ctx.setFontSize(item2.size);
} else {
this.ctx.font = `${item2.size}px sans-serif`; // 小程序端补全font属性
}
this.ctx.fillStyle = item2.color;
// 计算当前文本的绘制X坐标
let currentX = prevTextEndX;
// 非第一个文本加上配置的偏移量默认8px
if (index2 > 0) {
const offsetX = item2.offsetX || 6;
currentX += this.fitSize(offsetX); // 适配设备尺寸的偏移量
}
// 绘制文本
this.ctx.fillText(
item2.text,
currentX,
this.fitSize(item.coord[0][1])
);
// 更新前一个文本的结束位置当前文本的X + 文本宽度)
const textWidth = this.getTextWidth(item2.text, item2.size);
prevTextEndX = currentX + textWidth;
this.ctx.closePath();
});
}
});
},
// 新增计算文本的实际绘制宽度适配app/小程序)
getTextWidth(text, fontSize) {
if (this.type === 'app') {
// App端通过measureText计算
return this.ctx.measureText(text).width;
} else {
// 小程序端手动计算(更稳定)
this.ctx.font = `${fontSize}px sans-serif`;
return this.ctx.measureText(text).width;
}
},
drawImages(draw, canvas) {
let x,
y,
w,
h = 0
draw.forEach((item) => {
if (item.coord.length >= 2) x = item.coord[0][0]
y = item.coord[0][1]
w = item.coord[1][0]
h = item.coord[1][1]
if (this.type === 'wx') {
let img = canvas.createImage()
img.src = item.url
img.onload = () => {
this.ctx.drawImage(
img,
this.fitSize(item.coord[0][0]),
this.fitSize(item.coord[0][1]),
this.fitSize(item.coord[1][0]),
this.fitSize(item.coord[1][1])
)
this.drawLine(this.lineArr)
this.drawCircle(this.circleArr)
this.drawRect(this.rectArr)
this.drawText(this.textArr)
if (this.type === 'app') {
this.ctx.draw()
}
this.loading = false
}
} else {
this.ctx.drawImage(item.url, this.fitSize(x), this.fitSize(y), this.fitSize(w),
this
.fitSize(h))
}
})
},
drawLine(draw) {
draw.forEach((item) => {
let x1 = this.fitSize(item.coord[0][0])
let y1 = this.fitSize(item.coord[0][1])
let x2 = this.fitSize(item.coord[1][0])
let y2 = this.fitSize(item.coord[1][1])
this.ctx.beginPath()
this.ctx.moveTo(x1, y1)
this.ctx.lineTo(x2, y2)
if (this.type === 'app') {
this.ctx.setStrokeStyle(item.color)
this.ctx.setLineWidth(item.width)
} else {
this.ctx.strokeStyle = item.color
this.ctx.lineWidth = item.width
}
if (item.dash && item.dash.length > 0) {
//虚线
this.ctx.setLineDash(item.dash)
}
this.ctx.stroke()
this.ctx.closePath()
})
},
drawCircle(draw) {
draw.forEach((item) => {
let x = this.fitSize(item.coord[0][0])
let y = this.fitSize(item.coord[0][1])
let r = this.fitSize(item.r)
this.ctx.beginPath()
this.ctx.arc(x, y, r, 0, Math.PI * 2, false)
this.ctx.fillStyle = item.color
this.ctx.fill()
this.ctx.closePath()
})
},
fitSize(coordinate) {
let newWindowWidth = this.windowWidth;
const designWidth = 375; // 设计稿宽度
// 修正比例计算:设备宽度 / 设计稿宽度 = 缩放比例
const scale = newWindowWidth / designWidth;
// 设计稿的坐标 * 缩放比例 = 设备实际坐标
return coordinate * scale;
}
},
}
</script>
<style>
</style>