javascript - 纯新手用 vue 1.0 写了个简单的笔记本工具,有个小问题请教大神们

 h619718610 发布于 2022-11-06 17:10

开始用的2.0的脚手架,发现坑好多,忍不了换回1了。
断断续续大概写了半个月吧,大致的功能如下:

  • 基本的增删、编辑笔记

  • 按类别、标题、内容、时间戳进行过滤

  • 按创建时间或标题排序

  • 暂存未确认保存的笔记

  • 支持 markdown 和涂鸦两种记录方式

  • 用 localStorage 存取数据,不手动 clear 的话算是个本地的简易数据库吧

  • 移动端适配(虽然体验不如桌面……不过我也不是设计师,已经尽力了)

然后我的问题来了…桌面端编辑涂鸦完全没问题,但手机上就是不行,canvas 的 context 的 stroke 方法似乎失效了。我已经在控制台里打印了 mouse 和 touch 事件的坐标,麻烦大神们看看是什么原因吧…实在搞不懂了T_T

DEMO 在这里,源码在这里,感谢诸位

涂鸦的方法如下

// 初始化涂鸦编辑器
const initCanvas = (canvasEle, colorsEle, controllersEle, imageData) => {
  // 颜色值对象
  const colorTable = [
    {
      name: 'black',
      regularCode: '#222',
      opagueCode: 'rgb(189, 189, 189)',
    },
    {
      name: 'green',
      regularCode: '#5cb85c',
      opagueCode: 'rgb(206, 234, 206)',
    },
    {
      name: 'yellow',
      regularCode: '#f0ad4e',
      opagueCode: 'rgb(251, 231, 202)',
    },
    {
      name: 'red',
      regularCode: '#d9534f',
      opagueCode: 'rgb(244, 203, 202)',
    },
    {
      name: 'white',
      regularCode: '#fff',
      opagueCode: '#fff',
    },
  ];

  // 初始化 context
  let ctx = canvasEle.getContext('2d');
  let selectedColor = null;
  let hasOnGoingStroke = false;
  let prevStatusStack = [];
  let futureStatusStack = [];
  let $canvas = $(canvasEle);

  // 方法:将 imageData 写入一个 Image 对象,画在 canvas 上
  const loadImageData = (data) => {
    let img = new Image();
    img.src = data;
    img.onload = () => {
      clearCanvas();
      ctx.drawImage(img, 0, 0);
    };
  }
  // 方法:存储当前的 canvas 内容为 imageData,推入状态栈
  const saveImageData = () => {
    let currentStatus = canvasEle.toDataURL();
    prevStatusStack.unshift(currentStatus);
  };
  // 方法:清除画布
  const clearCanvas = () => {
    ctx.clearRect(0, 0, 260, 260);
  };
  // 若提供了 imageData 参数就画出来
  if (imageData !== null) {
    loadImageData(imageData);
  }
  // 然后无论此时是否已有内容,推入状态栈一次
  saveImageData();

  // 颜色选择器鼠标事件
  $(colorsEle).children('li')
    .on('click', function() {
      let $this = $(this);
      const tarColorName = $this.data('color');
      selectedColor = colorTable.find((item) => {
        return item.name === tarColorName;
      });
      $this
        .siblings('.current')
        .removeClass('current')
        .end()
        .addClass('current');
    })
    .siblings('[data-color=black]')
    .click();
  // canvas 鼠标事件
  $canvas.on('mousedown touchstart', (evt) => {
      let stX, stY;
      switch (evt.type) {
        case 'touchstart':
          stX = evt.targetTouches[0].clientX;
          stY = evt.targetTouches[0].clientY;
          break;
        default:
          stX = evt.offsetX;
          stY = evt.offsetY;
          break;
      }
      console.log('st', stX, stY);
      hasOnGoingStroke = true;
      ctx.strokeStyle = selectedColor.opagueCode;
      ctx.lineWidth = 5;
      ctx.lineCap = 'round';
      ctx.lineJoin = 'round';
      ctx.imageSmoothingEnabled = true;
      ctx.beginPath();
      ctx.moveTo(stX, stY);
    })
    .on('mousemove touchmove', (evt) => {
      if (hasOnGoingStroke === true) {
        let mdX, mdY;
        switch (evt.type) {
          case 'touchmove':
            mdX = evt.changedTouches[0].clientX;
            mdY = evt.changedTouches[0].clientY;
            break;
          default:
            mdX = evt.offsetX;
            mdY = evt.offsetY;
            break;
        }
        console.log('md', mdX, mdY);
        ctx.lineTo(mdX, mdY);
        ctx.stroke();
      }
    })
    .on('mouseout mouseup touchend', (evt) => {
      if (hasOnGoingStroke === true) {
        let edX, edY;
        switch (evt.type) {
          case 'touchend':
            edX = evt.changedTouches[0].clientX;
            edY = evt.changedTouches[0].clientY;
            break;
          default:
            edX = evt.offsetX;
            edY = evt.offsetY;
            break;
        }
        ctx.strokeStyle = selectedColor.regularCode;
        ctx.lineTo(edX, edY);
        ctx.stroke();
        console.log('ed', edX, edY);
        hasOnGoingStroke = false;
        saveImageData();
      }
    });

  // 控制器鼠标事件
  $(controllersEle)
    .children('.undo')
    .on('click', (evt) => {
      if (prevStatusStack.length !== 0) {
        loadImageData(prevStatusStack[0]);
        let currentStep = prevStatusStack.splice(0, 1);
        futureStatusStack.unshift(currentStep);
      }
    })
    .end()
    .children('.redo')
    .on('click', (evt) => {
      if (futureStatusStack.length !== 0) {
        loadImageData(futureStatusStack[0]);
        let currentStep = futureStatusStack.splice(0, 1);
        prevStatusStack.unshift(currentStep);
      }
    })
    .end()
    .children('.clear')
    .on('click', () => {
      clearCanvas();
    });
};
1 个回答
  • 已经自己解决,原因是

    Touch对象的 clientX 和 clientY 是相对于视口的,要取得点击画布的实际位置,要将这两个值减去画布相对于视口的坐标:

      x = evt.touches[0].clientX - $canvas.offset().left;
      y = evt.touches[0].clientY - $canvas.offset().top;
      ctx.moveTo(x, y);
    
    2022-11-12 01:56 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有