开始用的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(); }); };
已经自己解决,原因是
Touch对象的 clientX 和 clientY 是相对于视口的,要取得点击画布的实际位置,要将这两个值减去画布相对于视口的坐标:
x = evt.touches[0].clientX - $canvas.offset().left; y = evt.touches[0].clientY - $canvas.offset().top; ctx.moveTo(x, y);