如何使用vue插件Konva实现图片的缩放
前提问题:要完成数据标注的第一步,使用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>
<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,
})
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)
}
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.clientWidth
let newheight = sybs.value.clientHeight
state.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 layer
var layer = new Konva.Layer();
// add the layer to the stage
state.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 = 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()
}
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
})
})
onBeforeUnmount(() => {
window.removeEventListener('resize', getWidth);
})
</script>
<style lang="scss" scoped>
.container {
width: 100%;
height: 100%;
}
</style>
借鉴了很多博主的思路,有需要可以看一下其他博主的文章~直通车1 直通车2