mirror of
https://github.com/rough-stuff/rough.git
synced 2026-01-22 12:48:09 -05:00
129 lines
4.7 KiB
JavaScript
129 lines
4.7 KiB
JavaScript
import { RoughRenderer } from './renderer';
|
|
const hasDocument = typeof document !== 'undefined';
|
|
export class RoughCanvasBase {
|
|
constructor(canvas) {
|
|
this.canvas = canvas;
|
|
this.ctx = this.canvas.getContext('2d');
|
|
}
|
|
static createRenderer() {
|
|
return new RoughRenderer();
|
|
}
|
|
draw(drawable) {
|
|
const sets = drawable.sets || [];
|
|
const o = drawable.options || this.getDefaultOptions();
|
|
const ctx = this.ctx;
|
|
for (const drawing of sets) {
|
|
switch (drawing.type) {
|
|
case 'path':
|
|
ctx.save();
|
|
ctx.strokeStyle = o.stroke;
|
|
ctx.lineWidth = o.strokeWidth;
|
|
this._drawToContext(ctx, drawing);
|
|
ctx.restore();
|
|
break;
|
|
case 'fillPath':
|
|
ctx.save();
|
|
ctx.fillStyle = o.fill || '';
|
|
this._drawToContext(ctx, drawing);
|
|
ctx.restore();
|
|
break;
|
|
case 'fillSketch':
|
|
this.fillSketch(ctx, drawing, o);
|
|
break;
|
|
case 'path2Dfill': {
|
|
this.ctx.save();
|
|
this.ctx.fillStyle = o.fill || '';
|
|
const p2d = new Path2D(drawing.path);
|
|
this.ctx.fill(p2d);
|
|
this.ctx.restore();
|
|
break;
|
|
}
|
|
case 'path2Dpattern': {
|
|
const doc = this.canvas.ownerDocument || (hasDocument && document);
|
|
if (doc) {
|
|
const size = drawing.size;
|
|
const hcanvas = doc.createElement('canvas');
|
|
const hcontext = hcanvas.getContext('2d');
|
|
const bbox = this.computeBBox(drawing.path);
|
|
if (bbox && (bbox.width || bbox.height)) {
|
|
hcanvas.width = this.canvas.width;
|
|
hcanvas.height = this.canvas.height;
|
|
hcontext.translate(bbox.x || 0, bbox.y || 0);
|
|
}
|
|
else {
|
|
hcanvas.width = size[0];
|
|
hcanvas.height = size[1];
|
|
}
|
|
this.fillSketch(hcontext, drawing, o);
|
|
this.ctx.save();
|
|
this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat');
|
|
const p2d = new Path2D(drawing.path);
|
|
this.ctx.fill(p2d);
|
|
this.ctx.restore();
|
|
}
|
|
else {
|
|
console.error('Cannot render path2Dpattern. No defs/document defined.');
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
computeBBox(d) {
|
|
if (hasDocument) {
|
|
try {
|
|
const ns = 'http://www.w3.org/2000/svg';
|
|
const svg = document.createElementNS(ns, 'svg');
|
|
svg.setAttribute('width', '0');
|
|
svg.setAttribute('height', '0');
|
|
const pathNode = self.document.createElementNS(ns, 'path');
|
|
pathNode.setAttribute('d', d);
|
|
svg.appendChild(pathNode);
|
|
document.body.appendChild(svg);
|
|
const bbox = pathNode.getBBox();
|
|
document.body.removeChild(svg);
|
|
return bbox;
|
|
}
|
|
catch (err) { }
|
|
}
|
|
return null;
|
|
}
|
|
fillSketch(ctx, drawing, o) {
|
|
let fweight = o.fillWeight;
|
|
if (fweight < 0) {
|
|
fweight = o.strokeWidth / 2;
|
|
}
|
|
ctx.save();
|
|
ctx.strokeStyle = o.fill || '';
|
|
ctx.lineWidth = fweight;
|
|
this._drawToContext(ctx, drawing);
|
|
ctx.restore();
|
|
}
|
|
_drawToContext(ctx, drawing) {
|
|
ctx.beginPath();
|
|
for (const item of drawing.ops) {
|
|
const data = item.data;
|
|
switch (item.op) {
|
|
case 'move':
|
|
ctx.moveTo(data[0], data[1]);
|
|
break;
|
|
case 'bcurveTo':
|
|
ctx.bezierCurveTo(data[0], data[1], data[2], data[3], data[4], data[5]);
|
|
break;
|
|
case 'qcurveTo':
|
|
ctx.quadraticCurveTo(data[0], data[1], data[2], data[3]);
|
|
break;
|
|
case 'lineTo':
|
|
ctx.lineTo(data[0], data[1]);
|
|
break;
|
|
}
|
|
}
|
|
if (drawing.type === 'fillPath') {
|
|
ctx.fill();
|
|
}
|
|
else {
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
}
|