主要是参考下面的连接,加上一些本身的改进理解和 RxJS
版的实现。javascript
文章的做者好像是 Fabric.js
的做者html
下面是基础版本的画图代码,核心是监听几个鼠标事件。git
var el = document.getElementById('c'); var ctx = el.getContext('2d'); var isDrawing; el.onmousedown = function(e) { isDrawing = true; ctx.moveTo(e.clientX, e.clientY); }; el.onmousemove = function(e) { if (isDrawing) { ctx.lineTo(e.clientX, e.clientY); ctx.stroke(); } }; el.onmouseup = function() { isDrawing = false; }; 复制代码
须要改变画图线条,则须要设置 ctx
的一些属性github
ctx.lineWidth = 10; ctx.lineJoin = ctx.lineCap = 'round'; ctx.shadowBlur = 10; ctx.shadowColor = 'rgb(0, 0, 0)'; 复制代码
参考示例连接: codepen.io/kangax/pen/…canvas
上面画法存在的问题是:开始画的线条部分有点细和模糊,因为阴影部分的交叠,结束部分变粗。数组
基于点的方法markdown
参考示例连接: codepen.io/kangax/pen/…oop
这个方法是每次从新把以前的路径渲染一次,不是上面提到的画法,一点点的画线条。spa
鼠标移动时
ctx.clearRect(xxx)
ctx.beginPath()
ctx.lineTo(x, y)
画线ctx.stroke()
贝塞尔曲线版
主要是利用 ctx.quadraticCurveTo
。
参考示例连接: codepen.io/kangax/pen/…
参考文章中上面的代码存在一个问题: 点的位置计算,没有考虑 canvas 在文档中的位置。若是 canvas 元素不是在页面的开始位置,线条的位置就不对了。例如设置 canvas 左边距为 margin-left: 200px;
这里须要改进一下 x, y 值的计算方式
function getMousePos(canvas, evt) {
const rect = canvas.getBoundingClientRect();
return {
x: (evt.clientX - rect.left) / (rect.right - rect.left) * canvas.width,
y: (evt.clientY - rect.top) / (rect.bottom - rect.top) * canvas.height
}
}
// 也能够是
// x: evt.clientX - rect.left
复制代码
三个数据流: 按下鼠标,鼠标移动,松开鼠标
mouseDown$: X-------
mouseMove$: -A-B-C-D
mouseUp$: --------U-
mouseDown$.pipe(switchMap(xx)) 这样每次按下鼠标就会触发新的数据流分支
mouseMove$.pipe(takeUntil(xxx), pairwise()) 把触发松开鼠标以前的数据都取下,而后利用 pairwise
数据流变成了 -[A,B]-[B,C]-[C,D]-[D,U]-
const { fromEvent } = rxjs;
const {
map,
takeUntil,
pairwise,
switchMap
} = rxjs.operators;
const target = document.querySelector('#c')
const rect = target.getBoundingClientRect();
const ctx = target.getContext('2d');
ctx.lineWidth = 3;
ctx.lineCap = 'round';
ctx.strokeStyle = '#000';
const mouseDown$ = fromEvent(target, 'mousedown')
const mouseMove$ = fromEvent(target, 'mousemove')
const mouseUp$ = fromEvent(target, 'mouseup')
mouseDown$.pipe(
switchMap(e => {
return mouseMove$.pipe(
takeUntil(mouseUp$),
pairwise()
)
})
).subscribe(([preEvt, curEvt]) => {
if (preEvt) {
ctx.beginPath();
const pre = {
x: preEvt.clientX - rect.left,
y: preEvt.clientY - rect.top
}
const cur = {
x: curEvt.clientX - rect.left,
y: curEvt.clientY - rect.top
}
ctx.moveTo(pre.x, pre.y)
ctx.lineTo(cur.x, cur.y)
ctx.stroke()
}
});
复制代码