canvas实现对图片的标注(pc移动兼容),包括画任意线,画矩形,化圆,删除标注,撤销与反撤销,下载图片,改变画笔颜色,改变画笔大小
可以自己封装成组件使用
<template>
<van-overlay :show="show" class="h-100% w-100% display-flex justify-center items-center">
<div class=" h-auto">
<canvas id="canvas" ref="canvasRef" width="100%" height="300px" @mousedown="canvasDown($event)"
@mouseup="canvasUp($event)" @mousemove="canvasMove($event)" @touchstart="canvasDown($event)"
@touchend="canvasUp($event)" @touchmove="canvasMove($event)">
</canvas>
<!-- <img :src="signSrc" alt="" class=" w-full" /> -->
<div class="h-150px w-full bg-#fff">
<div class="w-90% m-auto h-40px bg-#fff display-flex justify-around items-center flex-wrap">
<!-- 曲线 -->
<div class="icon-div icon" @click="penClick">
<i class="iconfont" style="user-select: none;"></i>
</div>
<!-- 长方形 -->
<div class="icon-div icon" @click="rectangleClick">
<i class="iconfont " style="user-select: none;"></i>
</div>
<!-- 圆 -->
<div class="icon-div icon" @click="roundClick">
<i class="iconfont" style="user-select: none;"></i>
</div>
<!-- 左撤销 -->
<!-- 删除 -->
<div class="icon-div icon" @click="resetCanvas">
<i class="iconfont" style="user-select: none;"></i>
</div>
<!-- 下载 -->
<div class="icon-div icon" @click="saveImg">
<i class="iconfont" style="user-select: none;"> </i>
</div>
<div class="icon-div icon" @click="revoke">
<i class="iconfont" style="user-select: none;" :class="zuo == true ? '' : 'grey-redo'"
scale="4"></i>
</div>
<!-- 右撤销 -->
<div class="icon-div icon" @click="cancleRedo">
<i class="iconfont" style="user-select: none;" :class="you == true ? '' : 'grey-cancelRedo'"
scale="4"></i>
</div>
</div>
<div class="icon-div icon">
<div class=" w-90% m-auto">
<div class="text-#000">画笔大小</div>
<input type="range" id="lwRange" min="1" max="10" value="1" @change="LwRangeBtn" />
<div class="text-#000 p-t-5px p-b-5px">画笔颜色</div>
<input type="color" id="lcolor" value="#FF0000" @change="LcolorBtn" />
</div>
</div>
</div>
<!-- <div class="drawPane position-fixed top-62% left-15px" v-show="isShowDrawPane">
<div @click="isShowDrawPane = false" class="float-right">
<i class="iconfont"></i>
</div>
<div class="text-#000">画笔大小</div>
<input type="range" id="lwRange" min="1" max="10" value="1" @change="LwRangeBtn" />
<div class="text-#000 p-t-5px p-b-5px">画笔颜色</div>
<input type="color" id="lcolor" value="#FF1493" @change="LcolorBtn" />
</div> -->
</div>
</van-overlay>
</template>
<script lang="ts">
export default defineComponent({
name: "SignatureBoard",
components: {},
props: ['url'],
setup(props, ctx) {
const route = useRoute();
const router = useRouter();
const canvasRef = ref(null);
const data = reactive({
flag: false,
imgUrl: props.url,
img: new Image(),
context: {} as any,
oldX: 0,
oldY: 0,
penDraw: false,
rectangleDraw: false,
roundDraw: false,
arrowDraw: false,
signSrc: '',
show: true,
imageData: '',
cindex: 0,
isShowDrawPane: false,
history: [] as any,
zuo: false,
you: false,
});
const methodsMap = {
init() {
methodsMap.initDraw()
methodsMap.penClick()
},
isPc() {
const userAgentInfo = navigator.userAgent
const Agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod']
let flag = true
for (let v = 0; v < Agents.length; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false
break
}
}
return flag
},
initDraw() {
data.img.crossOrigin = ''
const canvas = document.getElementById("canvas")
var width = window.screen.width;
if (!canvas) {
return false
} else {
data.context = canvas.getContext('2d')
data.img.src = data.imgUrl
}
data.img.onload = () => {
if (data.img.complete) {
if (methodsMap.isPc()) {
canvas.setAttribute("width", '700')
canvas.setAttribute("height", (data.img.height * 700) / data.img.width)
data.context.drawImage(data.img, 0, 0, '700', (data.img.height * 700) / data.img.width)
} else {
canvas.setAttribute('width', window.innerWidth)
canvas.setAttribute('height', (window.innerWidth * data.img.height) / data.img.width)
data.context.drawImage(data.img, 0, 0, window.innerWidth, (window.innerWidth * data.img.height) / data.img.width)
}
data.context.lineWidth = 1,
data.context.strokeStyle = "red"
}
}
},
LwRangeBtn() {
data.context.lineWidth = parseInt(document.getElementById("lwRange")?.value)
},
LcolorBtn() {
data.context.fillStyle = document.getElementById("lcolor").value
data.context.strokeStyle = document.getElementById("lcolor").value
data.context.strokeStyle = document.getElementById("lcolor").value
},
penClick() {
data.penDraw = true
data.rectangleDraw = false
data.roundDraw = false
},
rectangleClick() {
data.penDraw = false
data.rectangleDraw = true
data.roundDraw = false
},
roundClick() {
data.penDraw = false
data.rectangleDraw = false
data.roundDraw = true
},
saveImg() {
const canvas = document.querySelector('canvas')
const imgBase64 = canvasRef.value.toDataURL('image/png')
data.img.crossOrigin = 'anonymous'
const date = Date.now().toString()
data.signSrc = imgBase64
let aLink = document.createElement('a')
let blob = this.base64ToBlob(imgBase64)
let evt = document.createEvent('HTMLEvents')
evt.initEvent('click', true, true)
aLink.download = `${date}.jpg`
aLink.href = URL.createObjectURL(blob)
aLink.click()
},
base64ToBlob(code: any) {
data.img.crossOrigin = ''
let parts = code.split(';base64,')
let contentType = parts[0].split(':')[1]
let raw = window.atob(parts[1])
let rawLength = raw.length
let uInt8Array = new Uint8Array(rawLength)
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i)
}
return new Blob([uInt8Array], { type: contentType })
},
pencil(e: any) {
const t = e.target
if (methodsMap.isPc()) {
const newx = e.offsetX
const newy = e.offsetY
data.context.lineTo(newx, newy)
} else {
const newx = e.changedTouches[0].clientX - t.parentNode.offsetLeft
const newy = e.changedTouches[0].clientY - t.parentNode.offsetTop
data.context.lineTo(newx, newy)
}
data.context.stroke()
},
rectangle(e: any) {
const t = e.target
if (methodsMap.isPc()) {
const newX = e.offsetX
const newY = e.offsetY
data.context.beginPath()
data.context.rect(data.oldX, data.oldY, newX - data.oldX, newY - data.oldY)
} else {
const newX = e.changedTouches[0].clientX - t.parentNode.offsetLeft
const newY = e.changedTouches[0].clientY - t.parentNode.offsetTop
data.context.beginPath()
data.context.rect(data.oldX, data.oldY, newX - data.oldX, newY - data.oldY)
}
data.context.stroke()
},
round(e: any) {
const t = e.target
if (methodsMap.isPc()) {
const newX = e.offsetX
const newY = e.offsetY
data.context.beginPath()
var r = Math.sqrt(Math.pow(newX - data.oldX, 2), Math.pow(newY - data.oldY), 2)
data.context.arc(data.oldX, data.oldY, r, 0, 2 * Math.PI)
} else {
const newX = e.changedTouches[0].clientX - t.parentNode.offsetLeft
const newY = e.changedTouches[0].clientY - t.parentNode.offsetTop
data.context.beginPath()
var r = Math.sqrt(Math.pow(newX - data.oldX, 2), Math.pow((newY - data.oldY)), 2)
data.context.arc(data.oldX, data.oldY, r, 0, 2 * Math.PI)
}
data.context.closePath()
data.context.stroke()
},
resetCanvas() {
data.history.length = 0
data.zuo = false
data.you = false
data.context.clearRect(0, 0, data.context.canvas.width, data.context.canvas.height)
data.history.length = 0
data.preDrawAry = []
data.nextDrawAry = []
data.middleAry = []
methodsMap.initDraw()
},
revoke() {
if (data.cindex <= 0) return
data.cindex--
data.you = true
data.context.putImageData(data.history[data.cindex], 0, 0)
if (data.cindex <= 0) {
data.zuo = false
}
},
cancleRedo() {
if (data.cindex >= data.history.length - 1) return
data.cindex++
data.zuo = true
data.context.putImageData(data.history[data.cindex], 0, 0)
if (data.cindex >= data.history.length - 1) {
data.you = false
}
},
canvasDown(e: any) {
data.flag = true
if (this.isPc()) {
data.oldX = e.offsetX
data.oldY = e.offsetY
} else {
data.oldX = e.changedTouches[0].clientX - e.target.parentNode.offsetLeft
data.oldY = e.changedTouches[0].clientY - e.target.parentNode.offsetTop
}
data.context.beginPath()
const preData = data.context.getImageData(0, 0, 600, 400)
data.history.splice(data.cindex + 1)
data.zuo = true
},
canvasMove(e: any) {
if (data.flag === true) {
if (data.history.length > 0) {
data.context.putImageData(
data.history[data.history.length - 1],
0,
0
)
} else {
data.history.push(data.context.getImageData(0, 0, 800, 600))
}
if (data.flag === true && data.penDraw === true) {
methodsMap.pencil(e)
}
if (data.flag === true && data.rectangleDraw === true) {
methodsMap.rectangle(e)
}
if (data.flag === true && data.roundDraw === true) {
methodsMap.round(e)
}
}
},
canvasUp(e: any) {
const canvas = document.querySelector('canvas')
const preData = data.context.getImageData(0, 0, 600, 400)
data.imageData = data.context.getImageData(0, 0, canvas.width, canvas.height)
data.history.push(data.context.getImageData(0, 0, 800, 600))
data.cindex = data.history.length - 1
data.flag = false
},
};
onMounted(() => {
methodsMap.init();
});
return {
...methodsMap,
...toRefs(data),
canvasRef
};
},
});
</script>