主要是參考下面的連接,加上一些本身的改進理解和 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()
}
});
複製代碼