当前位置: 首页 > article >正文

如何使用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


http://www.kler.cn/a/273980.html

相关文章:

  • Maven 详细配置:Maven settings 配置文件的详细说明
  • 初学STM32 --- USMART
  • 清除数字栈
  • 算法的学习笔记—不用常规控制语句求 1 到 n 的和
  • 深入解析-正则表达式
  • LeetCode:2274. 不含特殊楼层的最大连续楼层数(排序 Java)
  • 【代码随想录Day27】
  • 网页429:请求过多
  • 探索未来科技:量子计算的前沿与挑战
  • ET框架新起一个服务及实现服务之间的消息通讯
  • java毕业设计 | springboot+vue游戏交流网站(附源码)
  • 中国传统游戏-幻方-c/c++实现
  • 每天一个数据分析题(二百一十五)
  • 【leetcode】动态规划专题
  • html--蝴蝶
  • 生成微信小程序二维码
  • 由浅到深认识C语言(6):变量的存储类型
  • 如何在 docker 容器内部运行 docker命令
  • 活动报名 | 数能涌现,三生万物,长安链发布三周年庆暨生态年会邀您共聚
  • 微信公众号 H5本地调试配置 hosts + nginx + openssl
  • 鸿蒙Harmony应用开发—ArkTS声明式开发(绘制组件:Path)
  • AI将如何影响我们的生活?
  • 快速高效地数据分析处理:QtiPlot for Mac中文直装版 兼容M
  • Codeforces Round 932 (Div. 2) D. Exam in MAC【正难则反+容斥原理】
  • 【Unity】CatlikeCoding SRP
  • PHP反序列化--pop链