Vue怎么实现多点涂鸦效果

开发技术 作者:iii 2024-05-09 18:55:01
这篇文章主要介绍“Vue怎么实现多点涂鸦效果”,在日常操作中,相信很多人在Vue怎么实现多点涂鸦效果问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操�...

这篇文章主要介绍“Vue怎么实现多点涂鸦效果”,在日常操作中,相信很多人在Vue怎么实现多点涂鸦效果问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue怎么实现多点涂鸦效果”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

    创建画布

    创建画布并设置事件处理器

     <canvas
        ref="board"
        
        width="1000"
        height="1000"
        @touchstart="handleStart"
        @touchmove="handleMove"
        @touchend="handleEnd"
      />

    获取画布并初始化画笔信息

    onMounted(() => {
      initPointer();
    });
    
    const board = ref();
    const cxt = ref();
    const lineWidth = 4;
    
    const initPointer = () => {
      cxt.value = board.value.getContext('2d');
      window.MeApi?.mainWindowLoaded();
      cxt.value.strokeStyle = 'red';
      cxt.value.fillStyle = 'red';
      cxt.value.lineWidth = lineWidth;
    };

    触摸事件处理

    基础知识

    Touch.identifier

    唯一地识别和触摸平面接触的点的值。

    这个值在这根手指(或触摸笔等)所引发的所有事件中保持一致,直到它离开触摸平面。

    TouchEvent.changedTouches

    该属性返回一个TouchList:

    • 对于 touchstart 事件,这个 TouchList 对象列出在此次事件中新增加的触点。

    • 对于 touchmove 事件,列出和上一次事件相比较,发生了变化的触点。

    • 对于 touchend 事件,changedTouches 是已经从触摸面的离开的触点的集合(也就是说,手指已经离开了屏幕/触摸面)。

    拷贝触摸点

    浏览器会复用触摸点,通过拷贝只记录差异点和唯一标识符替换引用整个对象的方式进行优化

    type Point = {
      identifier: number,//触摸点什么标识
      //触摸点坐标
      x: number,
      y: number
    };
    
    const copyTouch = (touch: Touch) => {
      return {
        identifier: touch.identifier,
        x: touch.pageX,
        y: touch.pageY
      };
    };

    查找触摸点

    通过遍历 activityTouches 数组来寻找与给定标记相匹配的触摸点,返回该触摸点在数组中的下标。

    const activityTouchIndexById = (idToFind: number) => {
      for (let i = 0; i < activityTouches.length; i++) {
        const id = activityTouches[i].identifier;
    
        if (id === idToFind) {
          return i;
        }
      }
      return -1;
    };

    跟踪所有触摸点以实现多点触控

    //跟踪当前存在的所有触摸点
    const activityTouches: Point[] = [];

    新增触摸事件

    当屏幕上出现新的触摸点touchstart事件被触发,handleStart 函数被触发。此时,要收集记录触摸点并在触摸点处画圆:

    const handleStart = (evt: TouchEvent) => {
      //获取所有新增的点
      const touches = evt.changedTouches;
      for (let i = 0; i < touches.length; i++) {
        //收集触摸点
        activityTouches.push(copyTouch(touches[i]));
        //画圆
        cxt.value.beginPath();
        drawCircle(touches[i].clientX, touches[i].clientY)
      }
    };

    触摸移动时

    touchmove 事件被触发时,从而将调用handleMove() 函数,此时按照路径绘制线:

    const handleMove = (evt: TouchEvent) => {
      evt.preventDefault();
      const touches = evt.changedTouches;
      for (let i = 0; i < touches.length; i++) {
        const indexById = activityTouchIndexById(touches[i].identifier);
        if (indexById >= 0) {
          cxt.value.beginPath();
          cxt.value.moveTo(activityTouches[indexById].x, activityTouches[indexById].y);
          cxt.value.lineTo(touches[i].clientX, touches[i].pageY);
          cxt.value.stroke();
          //更新缓存信息
          activityTouches.splice(indexById, 1, copyTouch(touches[i]));
        }
      }
    };

    首先遍历所有发生移动的触摸点。通过读取每个触摸点的 Touch.identifier 属性,从缓存中读取每个触摸点在变化前的起点。这样取得每个触摸点之前位置的坐标,进而进行绘制。

    触摸结束处理

    通过调用 handleEnd() 函数来处理触摸结束事件:

    const handleEnd = (evt: TouchEvent) => {
      evt.preventDefault();
      const touches = evt.changedTouches;
      for (let i = 0; i < touches.length; i++) {
        const indexById = activityTouchIndexById(touches[i].identifier);
        if (indexById >= 0) {
          cxt.value.beginPath();
          cxt.value.moveTo(activityTouches[indexById].x, activityTouches[indexById].y);
          cxt.value.lineTo(touches[i].clientX, touches[i].clientY);
          drawCircle(touches[i].clientX, touches[i].clientY)
          //移除缓存
          activityTouches.splice(indexById, 1);
        }
      }
    };

    类似handleMove ,首先遍历所有事件,并读取缓存。在事件触发点画个圆,然后将对应的触摸对象从缓存中移除。

    其他

    移动端和PC端所对应的时间不同。详情可见鼠标事件和触摸事件文档。

    移动端的触摸点信息被封装在Touch中,通过Touch.clientXTouch.clientX 读取当前坐标

    移动端的触摸点信息被封装在MouseEvent中

    完整代码

    <template>
      <canvas
        ref="board"
        
        width="1000"
        height="1000"
        @touchstart="handleStart"
        @touchmove="handleMove"
        @touchend="handleEnd"
      ></canvas>
    </template>
    
    <script setup lang="ts">
    import { onMounted, ref } from 'vue';
    const board = ref();
    const cxt = ref();
    const lineWidth = 4;
    
    
    onMounted(() => {
      initPointer();
    });
    
    const initPointer = () => {
      cxt.value = board.value.getContext('2d');
      window.MeApi?.mainWindowLoaded();
      cxt.value.strokeStyle = 'red';
      cxt.value.fillStyle = 'red';
      cxt.value.lineWidth = lineWidth;
    };
    
    type Point = {
      identifier: number,
      x: number,
      y: number
    };
    
    const activityTouches: Point[] = [];
    
    const copyTouch = (touch: Touch) => {
      return {
        identifier: touch.identifier,
        x: touch.pageX,
        y: touch.pageY
      };
    };
    
    const activityTouchIndexById = (idToFind: number) => {
      for (let i = 0; i < activityTouches.length; i++) {
        const id = activityTouches[i].identifier;
    
        if (id === idToFind) {
          return i;
        }
      }
      return -1;
    };
    
    const handleStart = (evt: TouchEvent) => {
      const touches = evt.changedTouches;
      for (let i = 0; i < touches.length; i++) {
        activityTouches.push(copyTouch(touches[i]));
        cxt.value.beginPath();
        drawCircle(touches[i].clientX, touches[i].clientY)
      }
    };
    
    const handleMove = (evt: TouchEvent) => {
      evt.preventDefault();
      const touches = evt.changedTouches;
      for (let i = 0; i < touches.length; i++) {
        const indexById = activityTouchIndexById(touches[i].identifier);
        if (indexById >= 0) {
          cxt.value.beginPath();
          cxt.value.moveTo(activityTouches[indexById].x, activityTouches[indexById].y);
          cxt.value.lineTo(touches[i].clientX, touches[i].pageY);
          cxt.value.stroke();
          activityTouches.splice(indexById, 1, copyTouch(touches[i]));
        }
      }
    };
    
    const handleEnd = (evt: TouchEvent) => {
      evt.preventDefault();
      const touches = evt.changedTouches;
      for (let i = 0; i < touches.length; i++) {
        const indexById = activityTouchIndexById(touches[i].identifier);
        if (indexById >= 0) {
          cxt.value.beginPath();
          cxt.value.moveTo(activityTouches[indexById].x, activityTouches[indexById].y);
          cxt.value.lineTo(touches[i].clientX, touches[i].clientY);
          drawCircle(touches[i].clientX, touches[i].clientY)
          activityTouches.splice(indexById, 1);
        }
      }
    };
    
    const drawCircle = (x:number,y:number) => {
      cxt.value.arc(x, y, lineWidth / 2, 0, 2 * Math.PI, false);
      cxt.value.fill();
    }
    
    </script>

    到此,关于“Vue怎么实现多点涂鸦效果”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注捷杰建站网站,小编会继续努力为大家带来更多实用的文章!

    原创声明
    本站部分文章基于互联网的整理,我们会把真正“有用/优质”的文章整理提供给各位开发者。本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    本文链接:http://www.jiecseo.com/news/show_25749.html
    vue