前提问题:要完成数据标注的第一步,使用vue插件Konva实现图片的缩放
解决过程:首先安装插件Konva,再进行初始化,嵌入图片,Konva官网地址
解决结果:
1.安装Konva
npm install vue-konva konva --save
2. 创建区域
<template>
<div
id="canvas_container"
class="container"
@wheel="wheelForScale($event)"
ref ='sybs'
></div>
</template><script setup>
import { onMounted, onBeforeUnmount, reactive,ref ,nextTick} from 'vue'
import Konva from 'konva'
const state = reactive({
// 舞台对象
stage: null,
// 图片加载时就放大缩小的倍数,只是记录一下,后续不再更改此值
scaleX: 0,
scaleY: 0,
// 图片加载后,鼠标滚轮控制的放大缩小倍数
scaleByX: 1,
scaleByY: 1,
})
</script>
3.创建舞台 new Konva.Stage()
//注:初始化 Konva 时需要调用 Konva.Stage 构造函数
onMounted(() => {
state.stage = new Konva.Stage({
container: 'canvas_container', //id
width: sybs.value.clientWidth,
height: sybs.value.clientHeight,
});
window.addEventListener('resize', getWidth);
})
4. 创建背景区域
注:添加了 layer,Dom才会创建对应的layre canvas元素
onMounted(() => {
var layer = new Konva.Layer();
// add the layer to the stage
state.stage.add(layer);
})
5.绘制图片+可拖拽框
// 拖拽时的虚线框
let targetRect = new Konva.Rect({
x: 0,
y: 0,
width: 0,
height: 0,
stroke: 'red',
strokeWidth: 1 ,
dash: [20,10]
})
targetRect.hide()
layer.add(targetRect)const originImg = new Image()
originImg.src = "http://39.174.88.209:10791/aihang/img/2023-08-18/MISSION/PICTURE/8039_589763/2/2_ZOOM.jpg"originImg.onload = () => {
// 绘制图片
let imgWidth = originImg.width
_imgWidth = imgWidth
let imgHeight = originImg.height
_imgHeight = imgHeight
state.scaleX = state.stage.width() / imgWidth
state.scaleY = state.stage.height() / imgHeight
let backRect = new Konva.Rect({
width: state.stage.width(),
height: state.stage.height(),
fillPatternImage: originImg,
fillPatternScaleX: state.scaleX,
fillPatternScaleY: state.scaleY
})
_backRect = backRect
layer.add(backRect)
backRect.zIndex(0)
layer.draw()
}
6.放大或缩小图标方法
const wheelForScale = e => {
// 获取节点数据,等下用来获取鼠标的位置
const stage = state.stage
// 设置缩放比例,这个可以自己调,不过我觉得1.2倍挺合适的
const scaleBy = 1.2
// 判断鼠标滚轮的滚动方向
// 修改config中scale的缩放比例,用上一次的值乘以倍数就是放大,除以倍数就是缩小
if (e.deltaY < 0) {
state.scaleByX = state.scaleByX * scaleBy
state.scaleByY = state.scaleByY * scaleBy
} else {
state.scaleByX = state.scaleByX / scaleBy
state.scaleByY = state.scaleByY / scaleBy
}
if(state.scaleByX < 1 || state.scaleByY < 1){
state.scaleByX = 1
state.scaleByY = 1
stage.offsetX(0)
stage.offsetY(0)
return
}
// 获取鼠标位置
const pointer = stage.getPointerPosition()
// 这里用鼠标位置乘以放大的比例,得到放大后该点的位置,在减去放大前点所在的位置,得到偏移距离
const mousePointTo = {
x: pointer.x * (state.scaleByX - 1),
y: pointer.y * (state.scaleByY - 1)
}
// 放大
stage.scaleX(state.scaleByX)
stage.scaleY(state.scaleByY)
// 下面的这两句不加的话,不管鼠标放哪,都会以左上角为中心放大缩小,也就是x0,y0
// 用偏移距离除以放大倍数得到真正的偏移距离,这样缩放的时候就相当于一直在移动图片,实现根据鼠标位置来放大缩小图片
stage.offsetX(mousePointTo.x/state.scaleByX)
stage.offsetY(mousePointTo.y/state.scaleByY)
}
7.标注时鼠标的动作事件
state.stage.on("mousedown", (ev) => {
const pointer = state.stage.getPointerPosition()
_mousedownData = pointer
let rectX = _mousedownData.x / state.scaleByX + state.stage.offsetX()
let rectY = _mousedownData.y / state.scaleByY + state.stage.offsetY()
targetRect.x(rectX)
targetRect.y(rectY)
targetRect.show()
})
state.stage.on("mousemove",(ev) =>{
if(_mousedownData){
let pointer = state.stage.getPointerPosition()
let rectX = _mousedownData.x / state.scaleByX + state.stage.offsetX()
let rectY = _mousedownData.y / state.scaleByY + state.stage.offsetY()
let rectW = pointer.x / state.scaleByX - rectX + state.stage.offsetX()
let rectH = pointer.y / state.scaleByY - rectY + state.stage.offsetY()
targetRect.width(rectW)
targetRect.height(rectH)
}
})
state.stage.on("mouseup", (ev) => {
if(!_mousedownData){
return
}
let pointer = state.stage.getPointerPosition()
targetRect.width(0)
targetRect.height(0)
targetRect.hide()
// 获取的鼠标位置是不加偏移的
let rectX = _mousedownData.x / state.scaleByX + state.stage.offsetX()
let rectY = _mousedownData.y / state.scaleByY + state.stage.offsetY()
let rectW = pointer.x / state.scaleByX - rectX + state.stage.offsetX()
let rectH = pointer.y / state.scaleByY - rectY + state.stage.offsetY()let finalRect = new Konva.Rect({
x: rectX,
y: rectY,
width: rectW,
height: rectH,
stroke: 'red',
strokeWidth: 1,
})
layer.add(finalRect)
layer.draw()
_mousedownData = null
})
全部代码
<template><divid="canvas_container"class="container"@wheel="wheelForScale($event)" ref ='sybs'></div>
</template>
<script setup>
import { onMounted, onBeforeUnmount, reactive,ref ,nextTick} from 'vue'
import Konva from 'konva'
const state = reactive({// 舞台对象stage: null,// 图片加载时就放大缩小的倍数,只是记录一下,后续不再更改此值scaleX: 0,scaleY: 0,// 图片加载后,鼠标滚轮控制的放大缩小倍数scaleByX: 1,scaleByY: 1,
})
const wheelForScale = e => {// 获取节点数据,等下用来获取鼠标的位置const stage = state.stage// 设置缩放比例,这个可以自己调,不过我觉得1.2倍挺合适的const scaleBy = 1.2// 判断鼠标滚轮的滚动方向// 修改config中scale的缩放比例,用上一次的值乘以倍数就是放大,除以倍数就是缩小if (e.deltaY < 0) {state.scaleByX = state.scaleByX * scaleBystate.scaleByY = state.scaleByY * scaleBy} else {state.scaleByX = state.scaleByX / scaleBystate.scaleByY = state.scaleByY / scaleBy}if(state.scaleByX < 1 || state.scaleByY < 1){state.scaleByX = 1state.scaleByY = 1stage.offsetX(0)stage.offsetY(0)return}// 获取鼠标位置const pointer = stage.getPointerPosition()// 这里用鼠标位置乘以放大的比例,得到放大后该点的位置,在减去放大前点所在的位置,得到偏移距离const mousePointTo = {x: pointer.x * (state.scaleByX - 1),y: pointer.y * (state.scaleByY - 1)}// 放大stage.scaleX(state.scaleByX)stage.scaleY(state.scaleByY)// 下面的这两句不加的话,不管鼠标放哪,都会以左上角为中心放大缩小,也就是x0,y0// 用偏移距离除以放大倍数得到真正的偏移距离,这样缩放的时候就相当于一直在移动图片,实现根据鼠标位置来放大缩小图片stage.offsetX(mousePointTo.x/state.scaleByX)stage.offsetY(mousePointTo.y/state.scaleByY)
}
var _mousedownData = null
var _imgWidth = 0
var _imgHeight = 0
var _backRect = null
const sybs = ref(null);
const getWidth = () => {nextTick(()=>{if(sybs.value){let newwidth = sybs.value.clientWidthlet newheight = sybs.value.clientHeightstate.stage.width(newwidth)state.stage.height(newheight)_backRect.width(state.stage.width())_backRect.height(state.stage.height())_backRect.fillPatternScaleX(state.stage.width()/_imgWidth)_backRect.fillPatternScaleY(state.stage.height()/_imgHeight)}})
};
onMounted(() => {state.stage = new Konva.Stage({container: 'canvas_container', // id of container <div>width: sybs.value.clientWidth,height: sybs.value.clientHeight,});window.addEventListener('resize', getWidth);// then create layervar layer = new Konva.Layer();// add the layer to the stagestate.stage.add(layer);// 拖拽时的虚线框let targetRect = new Konva.Rect({x: 0,y: 0,width: 0,height: 0,stroke: 'red',strokeWidth: 1 ,dash: [20,10]})targetRect.hide()layer.add(targetRect)const originImg = new Image()originImg.src = "http://39.174.88.209:10791/aihang/img/2023-08-18/MISSION/PICTURE/8039_589763/2/2_ZOOM.jpg"originImg.onload = () => {// 绘制图片let imgWidth = originImg.width_imgWidth = imgWidthlet imgHeight = originImg.height_imgHeight = imgHeightstate.scaleX = state.stage.width() / imgWidthstate.scaleY = state.stage.height() / imgHeightlet backRect = new Konva.Rect({width: state.stage.width(),height: state.stage.height(),fillPatternImage: originImg,fillPatternScaleX: state.scaleX,fillPatternScaleY: state.scaleY})_backRect = backRectlayer.add(backRect)backRect.zIndex(0)layer.draw()}state.stage.on("mousedown", (ev) => {const pointer = state.stage.getPointerPosition()_mousedownData = pointerlet rectX = _mousedownData.x / state.scaleByX + state.stage.offsetX()let rectY = _mousedownData.y / state.scaleByY + state.stage.offsetY()targetRect.x(rectX)targetRect.y(rectY)targetRect.show()})state.stage.on("mousemove",(ev) =>{if(_mousedownData){let pointer = state.stage.getPointerPosition()let rectX = _mousedownData.x / state.scaleByX + state.stage.offsetX()let rectY = _mousedownData.y / state.scaleByY + state.stage.offsetY()let rectW = pointer.x / state.scaleByX - rectX + state.stage.offsetX()let rectH = pointer.y / state.scaleByY - rectY + state.stage.offsetY()targetRect.width(rectW)targetRect.height(rectH)}})state.stage.on("mouseup", (ev) => {if(!_mousedownData){return}let pointer = state.stage.getPointerPosition()targetRect.width(0)targetRect.height(0)targetRect.hide()// 获取的鼠标位置是不加偏移的let rectX = _mousedownData.x / state.scaleByX + state.stage.offsetX()let rectY = _mousedownData.y / state.scaleByY + state.stage.offsetY()let rectW = pointer.x / state.scaleByX - rectX + state.stage.offsetX()let rectH = pointer.y / state.scaleByY - rectY + state.stage.offsetY()let finalRect = new Konva.Rect({x: rectX,y: rectY,width: rectW,height: rectH,stroke: 'red',strokeWidth: 1,})layer.add(finalRect)layer.draw()_mousedownData = null})
})
onBeforeUnmount(() => {window.removeEventListener('resize', getWidth);
})
</script>
<style lang="scss" scoped>
.container {width: 100%;height: 100%;
}
</style>
借鉴了很多博主的思路,有需要可以看一下其他博主的文章~直通车1 直通车2