【uniapp】签名组件,兼容vue2vue3
网上找了个源码改吧改吧,清除了没用的功能和兼容性,基于uniapp开发的
样子
vue2 使用方法,具体的可以根据业务自行修改
<signature ref="signature" width="100%" height="410rpx"></signature>
confirm(){this.$refs['signature'].getCavasTempFile((url) => {//外部调用子组件的方法获取签名的base64})
}
vue2源码
<template><view class="canvas_wrapper" ref="canvas_wrappers" :style="canvasStyle"><canvas class="handWriting" disable-scroll="true" @touchstart="uploadScaleStart" @touchmove="uploadScaleMove"@touchend="uploadScaleEnd" canvas-id="handWriting"></canvas></view>
</template><script>export default {props: {width: {type: String,default: '100%'},height: {type: String,default: '200rpx'},},data() {return {canvasName: 'handWriting',ctx: '',canvasWidth: 0,canvasHeight: 0,transparent: 1, // 透明度selectColor: 'black',lineColor: '#1a1a1a', // 颜色lineSize: 1.5, // 笔迹倍数lineMin: 0.5, // 最小笔画半径lineMax: 8, // 最大笔画半径pressure: 1, // 默认压力smoothness: 60, //顺滑度,用60的距离来计算速度currentPoint: {},currentLine: [], // 当前线条firstTouch: true, // 第一次触发radius: 1, //画圆的半径cutArea: {top: 0,right: 0,bottom: 0,left: 0}, //裁剪区域bethelPoint: [], //保存所有线条 生成的贝塞尔点;lastPoint: 0,chirography: [], //笔迹currentChirography: {}, //当前笔迹linePrack: [], //划线轨迹 , 生成线条的实际点half: 0 //绘制一半点};},computed: {canvasStyle() {return `width:${this.widht};height:${this.height}`}},mounted() {this.containerEnter()},methods: {containerEnter() {let canvasName = this.canvasNamelet ctx = uni.createCanvasContext(canvasName, this)this.ctx = ctx;var query = uni.createSelectorQuery().in(this);query.select('.canvas_wrapper').boundingClientRect(rect => {this.canvasWidth = rect.width;this.canvasHeight = rect.height;/* 将canvas背景设置为 白底,不设置 导出的canvas的背景为透明 */this.setCanvasBg('rgba(0,0,0,0)');}).exec();},containerAfter() {this.retDraw()},// 获取临时文件getCavasTempFile(fn) {uni.canvasToTempFilePath({destWidth: 200,destHeight: 200,canvasId: 'handWriting',success: (res) => {//签名成功回调fn(res.tempFilePath)this.containerAfter()},fail: (err) => {console.log('图片临时文件生成失败', err);}}, this)},// 笔迹开始uploadScaleStart(e) {if (e.type != 'touchstart') return false;this.ctx.setFillStyle(this.lineColor); // 初始线条设置颜色this.ctx.setGlobalAlpha(this.transparent); // 设置半透明let currentPoint = {x: e.touches[0].x,y: e.touches[0].y}let currentLine = this.currentLine;currentLine.unshift({time: new Date().getTime(),dis: 0,x: currentPoint.x,y: currentPoint.y})this.currentPoint = currentPointif (this.firstTouch) {this.currentPoint = {top: currentPoint.y,right: currentPoint.x,bottom: currentPoint.y,left: currentPoint.x}this.firstTouch = false;}this.pointToLine(currentLine);},// 笔迹移动uploadScaleMove(e) {this.half++;if ((this.half % 3) != 0) {return false}if (e.type != 'touchmove') return false;if (e.cancelable) {// 判断默认行为是否已经被禁用if (!e.defaultPrevented) {e.preventDefault();}}let point = {x: e.touches[0].x,y: e.touches[0].y}//测试裁剪if (point.y < this.cutArea.top) {this.cutArea.top = point.y;}if (point.y < 0) this.cutArea.top = 0;if (point.x > this.cutArea.right) {this.cutArea.right = point.x;}if (this.canvasWidth - point.x <= 0) {this.cutArea.right = this.canvasWidth;}if (point.y > this.cutArea.bottom) {this.cutArea.bottom = point.y;}if (this.canvasHeight - point.y <= 0) {this.cutArea.bottom = this.canvasHeight;}if (point.x < this.cutArea.left) {this.cutArea.left = point.x;}if (point.x < 0) this.cutArea.left = 0;this.lastPoint = this.currentPointthis.currentPoint = pointlet currentLine = this.currentLinecurrentLine.unshift({time: new Date().getTime(),dis: this.distance(this.currentPoint, this.lastPoint),x: point.x,y: point.y})this.pointToLine(currentLine);},// 笔迹结束uploadScaleEnd(e) {if (e.type != 'touchend') return 0;let point = {x: e.changedTouches[0].x,y: e.changedTouches[0].y}this.lastPoint = this.currentPoint;this.currentPoint = point;let currentLine = this.currentLinecurrentLine.unshift({time: new Date().getTime(),dis: this.distance(this.currentPoint, this.lastPoint),x: point.x,y: point.y})if (currentLine.length > 2) {var info = (currentLine[0].time - currentLine[currentLine.length - 1].time) / currentLine.length;//$("#info").text(info.toFixed(2));}//一笔结束,保存笔迹的坐标点,清空,当前笔迹//增加判断是否在手写区域;this.pointToLine(currentLine);var currentChirography = {lineSize: this.lineSize,lineColor: this.lineColor};var chirography = this.chirographychirography.unshift(currentChirography);this.chirography = chirographyvar linePrack = this.linePracklinePrack.unshift(this.currentLine);this.half = 0;this.linePrack = linePrack;this.currentLine = [];},retDraw() {this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)this.ctx.draw();//设置canvas背景this.setCanvasBg('rgba(0,0,0,0)');},//画两点之间的线条;参数为:line,会绘制最近的开始的两个点;pointToLine(line) {this.calcBethelLine(line);return;},//计算插值的方式;calcBethelLine(line) {if (line.length <= 1) {line[0].r = this.radius;return;}let x0, x1, x2, y0, y1, y2, r0, r1, r2, len, lastRadius, dis = 0,time = 0,curveValue = 0.5;if (line.length <= 2) {x0 = line[1].xy0 = line[1].yx2 = line[1].x + (line[0].x - line[1].x) * curveValue;y2 = line[1].y + (line[0].y - line[1].y) * curveValue;x1 = x0 + (x2 - x0) * curveValue;y1 = y0 + (y2 - y0) * curveValue;;} else {x0 = line[2].x + (line[1].x - line[2].x) * curveValue;y0 = line[2].y + (line[1].y - line[2].y) * curveValue;x1 = line[1].x;y1 = line[1].y;x2 = x1 + (line[0].x - x1) * curveValue;y2 = y1 + (line[0].y - y1) * curveValue;}//从计算公式看,三个点分别是(x0,y0),(x1,y1),(x2,y2) ;(x1,y1)这个是控制点,控制点不会落在曲线上;实际上,这个点还会手写获取的实际点,却落在曲线上len = this.distance({x: x2,y: y2}, {x: x0,y: y0});lastRadius = this.radius;for (let n = 0; n < line.length - 1; n++) {dis += line[n].dis;time += line[n].time - line[n + 1].time;if (dis > this.smoothness) break;}this.radius = Math.min(time / len * this.pressure + this.lineMin, this.lineMax) * this.lineSizeline[0].r = this.radius;//计算笔迹半径;if (line.length <= 2) {r0 = (lastRadius + this.radius) / 2;r1 = r0;r2 = r1;//return;} else {r0 = (line[2].r + line[1].r) / 2;r1 = line[1].r;r2 = (line[1].r + line[0].r) / 2;}let n = 5;let point = [];for (let i = 0; i < n; i++) {let t = i / (n - 1);let x = (1 - t) * (1 - t) * x0 + 2 * t * (1 - t) * x1 + t * t * x2;let y = (1 - t) * (1 - t) * y0 + 2 * t * (1 - t) * y1 + t * t * y2;let r = lastRadius + (this.radius - lastRadius) / n * i;point.push({x: x,y: y,r: r});if (point.length == 3) {let a = this.ctaCalc(point[0].x, point[0].y, point[0].r, point[1].x, point[1].y, point[1].r, point[2].x, point[2].y, point[2].r);a[0].color = this.lineColor;this.bethelDraw(a, 1);point = [{x: x,y: y,r: r}];}}this.currentLine = line},//求两点之间距离distance(a, b) {let x = b.x - a.x;let y = b.y - a.y;return Math.sqrt(x * x + y * y);},ctaCalc(x0, y0, r0, x1, y1, r1, x2, y2, r2) {let a = [],vx01, vy01, norm, n_x0, n_y0, vx21, vy21, n_x2, n_y2;vx01 = x1 - x0;vy01 = y1 - y0;norm = Math.sqrt(vx01 * vx01 + vy01 * vy01 + 0.0001) * 2;vx01 = vx01 / norm * r0;vy01 = vy01 / norm * r0;n_x0 = vy01;n_y0 = -vx01;vx21 = x1 - x2;vy21 = y1 - y2;norm = Math.sqrt(vx21 * vx21 + vy21 * vy21 + 0.0001) * 2;vx21 = vx21 / norm * r2;vy21 = vy21 / norm * r2;n_x2 = -vy21;n_y2 = vx21;a.push({mx: x0 + n_x0,my: y0 + n_y0,color: "#1A1A1A"});a.push({c1x: x1 + n_x0,c1y: y1 + n_y0,c2x: x1 + n_x2,c2y: y1 + n_y2,ex: x2 + n_x2,ey: y2 + n_y2});a.push({c1x: x2 + n_x2 - vx21,c1y: y2 + n_y2 - vy21,c2x: x2 - n_x2 - vx21,c2y: y2 - n_y2 - vy21,ex: x2 - n_x2,ey: y2 - n_y2});a.push({c1x: x1 - n_x2,c1y: y1 - n_y2,c2x: x1 - n_x0,c2y: y1 - n_y0,ex: x0 - n_x0,ey: y0 - n_y0});a.push({c1x: x0 - n_x0 - vx01,c1y: y0 - n_y0 - vy01,c2x: x0 + n_x0 - vx01,c2y: y0 + n_y0 - vy01,ex: x0 + n_x0,ey: y0 + n_y0});a[0].mx = a[0].mx.toFixed(1);a[0].mx = parseFloat(a[0].mx);a[0].my = a[0].my.toFixed(1);a[0].my = parseFloat(a[0].my);for (let i = 1; i < a.length; i++) {a[i].c1x = a[i].c1x.toFixed(1);a[i].c1x = parseFloat(a[i].c1x);a[i].c1y = a[i].c1y.toFixed(1);a[i].c1y = parseFloat(a[i].c1y);a[i].c2x = a[i].c2x.toFixed(1);a[i].c2x = parseFloat(a[i].c2x);a[i].c2y = a[i].c2y.toFixed(1);a[i].c2y = parseFloat(a[i].c2y);a[i].ex = a[i].ex.toFixed(1);a[i].ex = parseFloat(a[i].ex);a[i].ey = a[i].ey.toFixed(1);a[i].ey = parseFloat(a[i].ey);}return a;},bethelDraw(point, is_fill, color) {let ctx = this.ctx;ctx.beginPath();ctx.moveTo(point[0].mx, point[0].my);if (undefined != color) {ctx.setFillStyle(color);ctx.setStrokeStyle(color);} else {ctx.setFillStyle(point[0].color);ctx.setStrokeStyle(point[0].color);}for (let i = 1; i < point.length; i++) {ctx.bezierCurveTo(point[i].c1x, point[i].c1y, point[i].c2x, point[i].c2y, point[i].ex, point[i].ey);}ctx.stroke();if (undefined != is_fill) {ctx.fill();}ctx.draw(true)},selectColorEvent(event) {var color = event.currentTarget.dataset.colorValue;var colorSelected = event.currentTarget.dataset.color;this.selectColor = colorSelected;this.lineColor = color;},setCanvasBg(color) {this.ctx.rect(0, 0, this.canvasWidth, this.canvasHeight - 4);this.ctx.setFillStyle(color)this.ctx.fill() //设置填充this.ctx.draw() //开画},},onShow() {}}
</script><style lang="scss" scoped>.canvas_wrapper {position: relative;background: #f8f7fc;border: 2px solid #EBEBEB;border-radius: 4px;margin: 0 auto;}.canvas_wrapper .handWriting {width: 100%;height: 100%;position: relative;z-index: 10;}.canvastips {position: absolute;top: 0;left: 0;width: 100%;height: 100%;z-index: 1;pointer-events: none;font-family: 'PingFang SC';font-style: normal;font-weight: 400;font-size: 70rpx;line-height: 180rpx;text-align: center;letter-spacing: 30rpx;color: #E6E6E6;}
</style>
vue3使用 由于是两个项目,操作也是不一样,所以获取图片的逻辑也有点不一样
<signatrue ref="signatrueRef" @confirmSign="confirmSign"></signatrue>
代码
<template><uni-popup ref="popup" type="bottom"><view class="page_container"><view class="canvas_title">请在这里签名</view><view class="canvas_wrapper" ref="canvas_wrappers"><canvas class="handWriting" disable-scroll="true" @touchstart="uploadScaleStart"@touchmove="uploadScaleMove" @touchend="uploadScaleEnd" canvas-id="handWriting"></canvas><view class="canvastips flex_center_center">请在这里签名</view></view><view class="zbutton_wrapper fixed_bottom"><view class="zbutton" @click="retDraw">重新签名</view><view class="zbutton black" @click="getCavasTempFile">完成签名并提交</view></view></view></uni-popup>
</template><script setup>import {ref,onMounted,computed,nextTick} from 'vue'let popup = ref(null)let state = ref({canvasName: 'handWriting',ctx: '',canvasWidth: 0,canvasHeight: 0,transparent: 1, // 透明度selectColor: 'black',lineColor: '#1a1a1a', // 颜色lineSize: 1.5, // 笔迹倍数lineMin: 0.5, // 最小笔画半径lineMax: 8, // 最大笔画半径pressure: 1, // 默认压力smoothness: 60, //顺滑度,用60的距离来计算速度currentPoint: {},currentLine: [], // 当前线条firstTouch: true, // 第一次触发radius: 1, //画圆的半径cutArea: {top: 0,right: 0,bottom: 0,left: 0}, //裁剪区域bethelPoint: [], //保存所有线条 生成的贝塞尔点;lastPoint: 0,chirography: [], //笔迹currentChirography: {}, //当前笔迹linePrack: [], //划线轨迹 , 生成线条的实际点half: 0 //绘制一半点})let canvasStyle = computed(() => {return `width:${state.value.widht};height:${state.value.height}`})onMounted(() => {// console.log(popup)})const init = () => {console.log('1111111111')popup.value.open('bottom');nextTick(() => {containerEnter()})}const containerEnter = () => {let canvasName = state.value.canvasNamelet ctx = uni.createCanvasContext(canvasName, this)state.value.ctx = ctx;var query = uni.createSelectorQuery().in(this);query.select('.canvas_wrapper').boundingClientRect(rect => {state.value.canvasWidth = rect.width;state.value.canvasHeight = rect.height;/* 将canvas背景设置为 白底,不设置 导出的canvas的背景为透明 */setCanvasBg('rgba(0,0,0,0)');}).exec();}const containerAfter = () => {retDraw()}// 获取临时文件const getCavasTempFile = (fn) => {// if (state.value.linePrack.length == 0 || state.value.linePrack[0].length <= 2) {// return state.value.$Tips({// title: '请签名'// })// }uni.canvasToTempFilePath({destWidth: 200,destHeight: 200,canvasId: 'handWriting',success: (res) => {//签名成功回调emits('confirmSign', res.tempFilePath)containerAfter()popup.value.close();},fail: (err) => {console.log('图片临时文件生成失败', err);}}, this)}// 笔迹开始const uploadScaleStart = (e) => {if (e.type != 'touchstart') return false;console.log('开始了');state.value.ctx.setFillStyle(state.value.lineColor); // 初始线条设置颜色state.value.ctx.setGlobalAlpha(state.value.transparent); // 设置半透明let currentPoint = {x: e.touches[0].x,y: e.touches[0].y}let currentLine = state.value.currentLine;currentLine.unshift({time: new Date().getTime(),dis: 0,x: currentPoint.x,y: currentPoint.y})state.value.currentPoint = currentPointif (state.value.firstTouch) {state.value.currentPoint = {top: currentPoint.y,right: currentPoint.x,bottom: currentPoint.y,left: currentPoint.x}state.value.firstTouch = false;}pointToLine(currentLine);}// 笔迹移动const uploadScaleMove = (e) => {state.value.half++;if ((state.value.half % 3) != 0) {return false}if (e.type != 'touchmove') return false;if (e.cancelable) {// 判断默认行为是否已经被禁用if (!e.defaultPrevented) {e.preventDefault();}}let point = {x: e.touches[0].x,y: e.touches[0].y}//测试裁剪if (point.y < state.value.cutArea.top) {state.value.cutArea.top = point.y;}if (point.y < 0) state.value.cutArea.top = 0;if (point.x > state.value.cutArea.right) {state.value.cutArea.right = point.x;}if (state.value.canvasWidth - point.x <= 0) {state.value.cutArea.right = state.value.canvasWidth;}if (point.y > state.value.cutArea.bottom) {state.value.cutArea.bottom = point.y;}if (state.value.canvasHeight - point.y <= 0) {state.value.cutArea.bottom = state.value.canvasHeight;}if (point.x < state.value.cutArea.left) {state.value.cutArea.left = point.x;}if (point.x < 0) state.value.cutArea.left = 0;state.value.lastPoint = state.value.currentPointstate.value.currentPoint = pointlet currentLine = state.value.currentLinecurrentLine.unshift({time: new Date().getTime(),dis: distance(state.value.currentPoint, state.value.lastPoint),x: point.x,y: point.y})pointToLine(currentLine);}// 笔迹结束const uploadScaleEnd = (e) => {console.log('结束了');if (e.type != 'touchend') return 0;let point = {x: e.changedTouches[0].x,y: e.changedTouches[0].y}state.value.lastPoint = state.value.currentPoint;state.value.currentPoint = point;let currentLine = state.value.currentLinecurrentLine.unshift({time: new Date().getTime(),dis: distance(state.value.currentPoint, state.value.lastPoint),x: point.x,y: point.y})if (currentLine.length > 2) {var info = (currentLine[0].time - currentLine[currentLine.length - 1].time) / currentLine.length;//$("#info").text(info.toFixed(2));}//一笔结束,保存笔迹的坐标点,清空,当前笔迹//增加判断是否在手写区域;pointToLine(currentLine);var currentChirography = {lineSize: state.value.lineSize,lineColor: state.value.lineColor};var chirography = state.value.chirographychirography.unshift(currentChirography);state.value.chirography = chirographyvar linePrack = state.value.linePracklinePrack.unshift(state.value.currentLine);state.value.half = 0;state.value.linePrack = linePrack;state.value.currentLine = [];}const retDraw = () => {state.value.ctx.clearRect(0, 0, state.value.canvasWidth, state.value.canvasHeight)state.value.ctx.draw();//设置canvas背景setCanvasBg('rgba(0,0,0,0)');}//画两点之间的线条;参数为:line,会绘制最近的开始的两个点;const pointToLine = (line) => {calcBethelLine(line);return;}//计算插值的方式;const calcBethelLine = (line) => {if (line.length <= 1) {line[0].r = state.value.radius;return;}let x0, x1, x2, y0, y1, y2, r0, r1, r2, len, lastRadius, dis = 0,time = 0,curveValue = 0.5;if (line.length <= 2) {x0 = line[1].xy0 = line[1].yx2 = line[1].x + (line[0].x - line[1].x) * curveValue;y2 = line[1].y + (line[0].y - line[1].y) * curveValue;x1 = x0 + (x2 - x0) * curveValue;y1 = y0 + (y2 - y0) * curveValue;;} else {x0 = line[2].x + (line[1].x - line[2].x) * curveValue;y0 = line[2].y + (line[1].y - line[2].y) * curveValue;x1 = line[1].x;y1 = line[1].y;x2 = x1 + (line[0].x - x1) * curveValue;y2 = y1 + (line[0].y - y1) * curveValue;}//从计算公式看,三个点分别是(x0,y0),(x1,y1),(x2,y2) ;(x1,y1)这个是控制点,控制点不会落在曲线上;实际上,这个点还会手写获取的实际点,却落在曲线上len = distance({x: x2,y: y2}, {x: x0,y: y0});lastRadius = state.value.radius;for (let n = 0; n < line.length - 1; n++) {dis += line[n].dis;time += line[n].time - line[n + 1].time;if (dis > state.value.smoothness) break;}state.value.radius = Math.min(time / len * state.value.pressure + state.value.lineMin, state.value.lineMax) *state.value.lineSizeline[0].r = state.value.radius;//计算笔迹半径;if (line.length <= 2) {r0 = (lastRadius + state.value.radius) / 2;r1 = r0;r2 = r1;//return;} else {r0 = (line[2].r + line[1].r) / 2;r1 = line[1].r;r2 = (line[1].r + line[0].r) / 2;}let n = 5;let point = [];for (let i = 0; i < n; i++) {let t = i / (n - 1);let x = (1 - t) * (1 - t) * x0 + 2 * t * (1 - t) * x1 + t * t * x2;let y = (1 - t) * (1 - t) * y0 + 2 * t * (1 - t) * y1 + t * t * y2;let r = lastRadius + (state.value.radius - lastRadius) / n * i;point.push({x: x,y: y,r: r});if (point.length == 3) {let a = ctaCalc(point[0].x, point[0].y, point[0].r, point[1].x, point[1].y, point[1].r,point[2].x, point[2].y, point[2].r);a[0].color = state.value.lineColor;bethelDraw(a, 1);point = [{x: x,y: y,r: r}];}}state.value.currentLine = line}//求两点之间距离const distance = (a, b) => {let x = b.x - a.x;let y = b.y - a.y;return Math.sqrt(x * x + y * y);}const ctaCalc = (x0, y0, r0, x1, y1, r1, x2, y2, r2) => {let a = [],vx01, vy01, norm, n_x0, n_y0, vx21, vy21, n_x2, n_y2;vx01 = x1 - x0;vy01 = y1 - y0;norm = Math.sqrt(vx01 * vx01 + vy01 * vy01 + 0.0001) * 2;vx01 = vx01 / norm * r0;vy01 = vy01 / norm * r0;n_x0 = vy01;n_y0 = -vx01;vx21 = x1 - x2;vy21 = y1 - y2;norm = Math.sqrt(vx21 * vx21 + vy21 * vy21 + 0.0001) * 2;vx21 = vx21 / norm * r2;vy21 = vy21 / norm * r2;n_x2 = -vy21;n_y2 = vx21;a.push({mx: x0 + n_x0,my: y0 + n_y0,color: "#1A1A1A"});a.push({c1x: x1 + n_x0,c1y: y1 + n_y0,c2x: x1 + n_x2,c2y: y1 + n_y2,ex: x2 + n_x2,ey: y2 + n_y2});a.push({c1x: x2 + n_x2 - vx21,c1y: y2 + n_y2 - vy21,c2x: x2 - n_x2 - vx21,c2y: y2 - n_y2 - vy21,ex: x2 - n_x2,ey: y2 - n_y2});a.push({c1x: x1 - n_x2,c1y: y1 - n_y2,c2x: x1 - n_x0,c2y: y1 - n_y0,ex: x0 - n_x0,ey: y0 - n_y0});a.push({c1x: x0 - n_x0 - vx01,c1y: y0 - n_y0 - vy01,c2x: x0 + n_x0 - vx01,c2y: y0 + n_y0 - vy01,ex: x0 + n_x0,ey: y0 + n_y0});a[0].mx = a[0].mx.toFixed(1);a[0].mx = parseFloat(a[0].mx);a[0].my = a[0].my.toFixed(1);a[0].my = parseFloat(a[0].my);for (let i = 1; i < a.length; i++) {a[i].c1x = a[i].c1x.toFixed(1);a[i].c1x = parseFloat(a[i].c1x);a[i].c1y = a[i].c1y.toFixed(1);a[i].c1y = parseFloat(a[i].c1y);a[i].c2x = a[i].c2x.toFixed(1);a[i].c2x = parseFloat(a[i].c2x);a[i].c2y = a[i].c2y.toFixed(1);a[i].c2y = parseFloat(a[i].c2y);a[i].ex = a[i].ex.toFixed(1);a[i].ex = parseFloat(a[i].ex);a[i].ey = a[i].ey.toFixed(1);a[i].ey = parseFloat(a[i].ey);}return a;}const bethelDraw = (point, is_fill, color) => {let ctx = state.value.ctx;ctx.beginPath();ctx.moveTo(point[0].mx, point[0].my);if (undefined != color) {ctx.setFillStyle(color);ctx.setStrokeStyle(color);} else {ctx.setFillStyle(point[0].color);ctx.setStrokeStyle(point[0].color);}for (let i = 1; i < point.length; i++) {ctx.bezierCurveTo(point[i].c1x, point[i].c1y, point[i].c2x, point[i].c2y, point[i].ex, point[i].ey);}ctx.stroke();if (undefined != is_fill) {ctx.fill();}ctx.draw(true)}const selectColorEvent = (event) => {var color = event.currentTarget.dataset.colorValue;var colorSelected = event.currentTarget.dataset.color;state.value.selectColor = colorSelected;state.value.lineColor = color;}const setCanvasBg = (color) => {state.value.ctx.rect(0, 0, state.value.canvasWidth, state.value.canvasHeight - 4);// ctx.setFillStyle('red')state.value.ctx.setFillStyle(color)state.value.ctx.fill() //设置填充state.value.ctx.draw() //开画}const emits = defineEmits(['confirmSign'])defineExpose({init})
</script><style lang="scss" scoped>.page_container {height: calc(702rpx + 140rpx + 106rpx + env(safe-area-inset-bottom));background: #fff;}.canvas_wrapper {position: relative;width: 702rpx;height: 702rpx;background: #F6F6F6;border: 2px solid #EBEBEB;border-radius: 4px;margin: 0 auto;}.canvas_wrapper .handWriting {width: 100%;height: 100%;position: relative;z-index: 10;}.canvastips {position: absolute;top: 0;left: 0;width: 100%;height: 100%;z-index: 1;pointer-events: none;font-family: 'PingFang SC';font-style: normal;font-weight: 400;font-size: 70rpx;line-height: 180rpx;text-align: center;letter-spacing: 30rpx;color: #E6E6E6;/* transform: rotate(90deg); */}.canvas_title {height: 120rpx;padding: 0 32rpx;font-family: 'PingFang SC';font-style: normal;font-weight: 500;font-size: 36rpx;line-height: 120rpx;mix-blend-mode: normal;opacity: 0.9;}
</style>
相关文章:

【uniapp】签名组件,兼容vue2vue3
网上找了个源码改吧改吧,清除了没用的功能和兼容性,基于uniapp开发的 样子 vue2 使用方法,具体的可以根据业务自行修改 <signature ref"signature" width"100%" height"410rpx"></signature>confi…...

初步利用Ansible实现批量服务器自动化管理
1.Ansible介绍 Ansible是一款开源的自动化运维工具, 在2012年由Michael DeHaan创建, 现在由Red Hat维护。Ansible是基于Python开发的,采用YAML语言编写自动化脚本playbook, 可以在Linux、Unix等系统上运行, 通过SSH协议管理节点, 无需在被管理节点安装agent。Ansible以其简单、…...
网络安全和隐私保护技术
一、定义 网络安全和隐私保护技术是指在互联网和其他网络环境中,通过技术手段保护网络系统、网络数据和用户隐私免于受到恶意攻击、非法访问、窃取或滥用。网络安全和隐私保护技术是保护网络安全和用户隐私的重要手段,是保障互联网和其他网络环境正常运…...
保险行业采购管理痛点及解决方案(数智化采购系统)
随着社会发展,个人和企业有了更多的金融保险需求。对于金融保险公司而言,需要在采购合规的基础上,基于数智化能力,让经营变得更加高效和智能。 1、围绕重点领域,业务加速布局。 保险行业结合自身业务经营重点&#x…...

光学仿真 | 仿真推动以人类视觉感知为本的汽车显示设计
如果产品设计无法使终端用户产生共鸣,就不会存在卓越的工程设计。您可以设计一种结构坚固的方向盘,但如果它被放在错误的位置,就无法实现其用于转向的主要目的。 同样,在围绕人类视觉进行设计时,显示器其实无需具备尽…...

判断两个对象是否不相等operator.ne()
【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 判断两个对象是否不相等 operator.ne() 选择题 下列代码执行输出的结果是? import operator print("【执行】operator.ne(8,8)") print(operator.ne(8,8)) print("【执行】…...

2023年云计算发展趋势:生活的智能未来
目录 引言1 智能家居的崭新时代2 无人驾驶的崭新时代3 虚拟现实的扩展与改进4 人工智能的综合应用5 云计算的可持续性结语 引言 时光荏苒,科技的飞速发展已经成为当今社会的标志之一。在这个数字化时代,云计算已经成为推动技术革新和生活方式改变的关键…...

Spring Boot项目中通过 Jasypt 对属性文件中的账号密码进行加密
下面是在Spring Boot项目中对属性文件中的账号密码进行加密的完整步骤,以MySQL的用户名为root,密码为123321为例: 步骤1:引入Jasypt依赖 在项目的pom.xml文件中,添加Jasypt依赖: <dependency><…...
2.3 矩阵消元
一、消元矩阵 消元矩阵执行消元步骤用到的矩阵。从第 i i i 个方程减去 l i j l_{ij} lij 乘第 j j j 个方程(将 x j x_j xj 从第 i i i 行中消去)。我们需要很多个简单的矩阵 E i j E_{ij} Eij,每一个对应一个主对角线下方要消…...
Docker 从构建开始导出一个镜像
docker build docker build命令用于从Dockerfile创建一个镜像。它的基本格式如下: docker build [OPTIONS] PATH | URL | -这里的PATH是Dockerfile所在的路径,URL是一个Git仓库地址,-表示从标准输入读取Dockerfile。 docker build命令的一…...

案例研究|腾讯音乐娱乐集团与JumpServer共探安全运维审计解决方案
近年来,得益于人民消费水平的提升以及版权意识的加强,用户付费意愿和在线用户数量持续增长,中国在线音乐市场呈现出稳定增长的发展态势。随着腾讯音乐于2018年12月上市,进一步推动了中国在线音乐市场的发展。 腾讯音乐娱乐集团&a…...

如何卸载在linux下通过rpm安装的mysql
目录 1.先关闭MySQL服务并查看运行状态 2.使用 rpm 管道命令的方式查看已安装的mysql 3. 使用rpm -ev 命令移除安装 4. 删除MySQL数据库内容 1.先关闭MySQL服务并查看运行状态 如果之前安装过并已经启动,则需要卸载前请先关闭MySQL服务 systemctl stop mysqld…...

docker复制镜像文件
一、复制镜像 #1. 查找本机已有的镜像docker images |grep xxxx#2. 将镜像复制出来指向到xxxx.tar的文件中 docker save 343cca04e31d > xxxx.tareg: 二、加载镜像 直接将拷贝好的镜像包直接加载即可 docker load < myimage.tar...

自动驾驶学习笔记(六)——Apollo安装
#Apollo开发者# 学习课程的传送门如下,当您也准备学习自动驾驶时,可以和我一同前往: 《自动驾驶新人之旅》免费课程—> 传送门 《2023星火培训【感知专项营】》免费课程—>传送门 文章目录 前言 Apollo安装 硬件配置 安装Ubuntu…...

四阶龙格库塔与元胞自动机
龙格库塔法参考: 【精选】四阶龙格库塔算法及matlab代码_四阶龙格库塔法matlab_漫道长歌行的博客-CSDN博客 龙格库塔算法 Runge Kutta Method及其Matlab代码_龙格库塔法matlab_Lzh_023016的博客-CSDN博客 元胞自动机参考: 元胞自动机:森林…...
Mac安装opencvJava踩坑
SpringBoot导入opencv依赖 先将jar包添加到libraries中在resources目录下创建lib文件夹并复制jar包到这里添加如下依赖,并刷新maven <dependency><groupId>org.opencv</groupId><artifactId>opencv</artifactId><version>4.8.0…...

YOLOv8-Pose推理详解及部署实现
目录 前言一、YOLOv8-Pose推理(Python)1. YOLOv8-Pose预测2. YOLOv8-Pose预处理3. YOLOv8-Pose后处理4. YOLOv8-Pose推理 二、YOLOv8-Pose推理(C)1. ONNX导出2. YOLOv8-Pose预处理3. YOLOv8-Pose后处理4. YOLOv8-Pose推理 三、YOLOv8-Pose部署1. 源码下载2. 环境配置2.1 配置CM…...

django+drf+vue 简单系统搭建 (1) - django创建项目
本系列文章为了记录自己第一个系统生成过程,主要使用django,drf,vue。本人非专业人士,此文只为记录学习,若有部分描述不够准确的地方,烦请指正。 建立这个系统的原因是因为,在生活中,很多觉得可以一两行代码…...
各种NoSQL数据库
NoSQL数据库是一类非关系型数据库,它们在数据存储和检索方面与传统的关系型数据库不同。不同类型的NoSQL数据库适用于不同的使用场景,因为它们具有各自的特点。以下是一些主要类型的NoSQL数据库及其特性和使用场景: 键值存储数据库 代表性数据…...

基于8086家具门安全控制系统设计
**单片机设计介绍,基于8086家具门安全控制系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 # 8086家具门安全控制系统设计介绍 8086家具门安全控制系统是一种用于保护家具和保证室内安全的系统。该系统基于808…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...