react-canvas-draw
Advanced tools
Comparing version 0.1.9 to 1.0.0-alpha.1
637
es/index.js
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
var _class, _temp; | ||
@@ -11,91 +13,253 @@ | ||
import React, { Component } from "react"; | ||
import React, { PureComponent } from "react"; | ||
import { LazyBrush } from "lazy-brush"; | ||
import { Catenary } from "catenary-curve"; | ||
var _default = (_temp = _class = function (_Component) { | ||
_inherits(_default, _Component); | ||
import ResizeObserver from "resize-observer-polyfill"; | ||
import drawImage from "./drawImage"; | ||
function midPointBtw(p1, p2) { | ||
return { | ||
x: p1.x + (p2.x - p1.x) / 2, | ||
y: p1.y + (p2.y - p1.y) / 2 | ||
}; | ||
} | ||
var canvasStyle = { | ||
display: "block", | ||
position: "absolute" | ||
}; | ||
var canvasTypes = [{ | ||
name: "interface", | ||
zIndex: 15 | ||
}, { | ||
name: "drawing", | ||
zIndex: 11 | ||
}, { | ||
name: "temp", | ||
zIndex: 12 | ||
}, { | ||
name: "grid", | ||
zIndex: 10 | ||
}]; | ||
var _default = (_temp = _class = function (_PureComponent) { | ||
_inherits(_default, _PureComponent); | ||
function _default(props) { | ||
_classCallCheck(this, _default); | ||
var _this = _possibleConstructorReturn(this, _Component.call(this, props)); | ||
var _this = _possibleConstructorReturn(this, _PureComponent.call(this, props)); | ||
_this.drawImage = function () { | ||
if (!_this.props.imgSrc) return; | ||
// Load the image | ||
_this.image = new Image(); | ||
_this.image.src = _this.props.imgSrc; | ||
// Draw the image once loaded | ||
_this.image.onload = function () { | ||
return drawImage({ ctx: _this.ctx.grid, img: _this.image }); | ||
}; | ||
}; | ||
_this.undo = function () { | ||
var lines = _this.lines.slice(0, -1); | ||
_this.clear(); | ||
_this.simulateDrawingLines({ lines: lines, immediate: true }); | ||
}; | ||
_this.getSaveData = function () { | ||
// Construct and return the saveData object | ||
var saveData = { | ||
linesArray: _this.linesArray, | ||
lines: [].concat(_this.lines), | ||
width: _this.props.canvasWidth, | ||
height: _this.props.canvasHeight | ||
}; | ||
return JSON.stringify(saveData); | ||
return saveData; | ||
}; | ||
_this.loadSaveData = function (saveData, immediate) { | ||
try { | ||
if (typeof saveData !== "string") { | ||
throw new Error("saveData needs to be a stringified array!"); | ||
} | ||
// parse first to catch any possible errors before clear() | ||
if ((typeof saveData === "undefined" ? "undefined" : _typeof(saveData)) !== "object") { | ||
throw new Error("saveData needs to be of type object!"); | ||
} | ||
var _JSON$parse = JSON.parse(saveData), | ||
linesArray = _JSON$parse.linesArray, | ||
width = _JSON$parse.width, | ||
height = _JSON$parse.height; | ||
var lines = saveData.lines, | ||
width = saveData.width, | ||
height = saveData.height; | ||
if (!linesArray || typeof linesArray.push !== "function") { | ||
throw new Error("linesArray needs to be an array!"); | ||
} | ||
// start the load-process | ||
_this.clear(); | ||
if (!lines || typeof lines.push !== "function") { | ||
throw new Error("saveData.lines needs to be an array!"); | ||
} | ||
if (width === _this.props.canvasWidth && height === _this.props.canvasHeight) { | ||
_this.linesArray = linesArray; | ||
} else { | ||
// we need to rescale the lines based on saved & current dimensions | ||
var scaleX = _this.props.canvasWidth / width; | ||
var scaleY = _this.props.canvasHeight / height; | ||
var scaleAvg = (scaleX + scaleY) / 2; | ||
_this.clear(); | ||
_this.linesArray = linesArray.map(function (line) { | ||
if (width === _this.props.canvasWidth && height === _this.props.canvasHeight) { | ||
_this.simulateDrawingLines({ | ||
lines: lines, | ||
immediate: immediate | ||
}); | ||
} else { | ||
// we need to rescale the lines based on saved & current dimensions | ||
var scaleX = _this.props.canvasWidth / width; | ||
var scaleY = _this.props.canvasHeight / height; | ||
var scaleAvg = (scaleX + scaleY) / 2; | ||
_this.simulateDrawingLines({ | ||
lines: lines.map(function (line) { | ||
return _extends({}, line, { | ||
endX: line.endX * scaleX, | ||
endY: line.endY * scaleY, | ||
startX: line.startX * scaleX, | ||
startY: line.startY * scaleY, | ||
size: line.size * scaleAvg | ||
points: line.points.map(function (p) { | ||
return { | ||
x: p.x * scaleX, | ||
y: p.y * scaleY | ||
}; | ||
}), | ||
brushRadius: line.brushRadius * scaleAvg | ||
}); | ||
}); | ||
} | ||
_this.redraw(immediate); | ||
} catch (err) { | ||
throw err; | ||
}), | ||
immediate: immediate | ||
}); | ||
} | ||
}; | ||
_this.redraw = function (immediate) { | ||
if (_this.ctx) { | ||
_this.ctx.clearRect(0, 0, _this.props.canvasWidth, _this.props.canvasHeight); | ||
} | ||
_this.simulateDrawingLines = function (_ref) { | ||
var lines = _ref.lines, | ||
immediate = _ref.immediate; | ||
_this.timeoutValidity++; | ||
var timeoutValidity = _this.timeoutValidity; | ||
_this.linesArray.forEach(function (line, idx) { | ||
// draw the line with a time offset | ||
// creates the cool drawing-animation effect | ||
if (!immediate) { | ||
// Simulate live-drawing of the loaded lines | ||
var curTime = 0; | ||
var timeoutGap = immediate ? 0 : _this.props.loadTimeOffset; | ||
lines.forEach(function (line) { | ||
var points = line.points, | ||
brushColor = line.brushColor, | ||
brushRadius = line.brushRadius; | ||
var _loop = function _loop(i) { | ||
curTime += timeoutGap; | ||
window.setTimeout(function () { | ||
if (timeoutValidity === _this.timeoutValidity) { | ||
_this.drawLine(line); | ||
} | ||
}, idx * _this.props.loadTimeOffset); | ||
} else { | ||
// if the immediate flag is true, draw without timeout | ||
_this.drawLine(line); | ||
_this.drawPoints({ | ||
points: points.slice(0, i + 1), | ||
brushColor: brushColor, | ||
brushRadius: brushRadius | ||
}); | ||
}, curTime); | ||
}; | ||
for (var i = 1; i < points.length; i++) { | ||
_loop(i); | ||
} | ||
curTime += timeoutGap; | ||
window.setTimeout(function () { | ||
// Save this line with its props instead of this.props | ||
_this.points = points; | ||
_this.saveLine({ brushColor: brushColor, brushRadius: brushRadius }); | ||
}, curTime); | ||
}); | ||
}; | ||
_this.getMousePos = function (e) { | ||
var rect = _this.canvas.getBoundingClientRect(); | ||
_this.handleTouchStart = function (e) { | ||
var _this$getPointerPos = _this.getPointerPos(e), | ||
x = _this$getPointerPos.x, | ||
y = _this$getPointerPos.y; | ||
_this.lazy.update({ x: x, y: y }, { both: true }); | ||
_this.handleMouseDown(e); | ||
_this.mouseHasMoved = true; | ||
}; | ||
_this.handleTouchMove = function (e) { | ||
e.preventDefault(); | ||
var _this$getPointerPos2 = _this.getPointerPos(e), | ||
x = _this$getPointerPos2.x, | ||
y = _this$getPointerPos2.y; | ||
_this.handlePointerMove(x, y); | ||
}; | ||
_this.handleTouchEnd = function (e) { | ||
_this.handleMouseUp(e); | ||
var brush = _this.lazy.getBrushCoordinates(); | ||
_this.lazy.update({ x: brush.x, y: brush.y }, { both: true }); | ||
_this.mouseHasMoved = true; | ||
}; | ||
_this.handleMouseDown = function (e) { | ||
e.preventDefault(); | ||
_this.isPressing = true; | ||
}; | ||
_this.handleMouseMove = function (e) { | ||
var _this$getPointerPos3 = _this.getPointerPos(e), | ||
x = _this$getPointerPos3.x, | ||
y = _this$getPointerPos3.y; | ||
_this.handlePointerMove(x, y); | ||
}; | ||
_this.handleMouseUp = function (e) { | ||
e.preventDefault(); | ||
_this.isDrawing = false; | ||
_this.isPressing = false; | ||
_this.saveLine(); | ||
}; | ||
_this.handleCanvasResize = function (entries, observer) { | ||
_this.dpi = window.devicePixelRatio; | ||
for (var _iterator = entries, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { | ||
var _ref2; | ||
if (_isArray) { | ||
if (_i >= _iterator.length) break; | ||
_ref2 = _iterator[_i++]; | ||
} else { | ||
_i = _iterator.next(); | ||
if (_i.done) break; | ||
_ref2 = _i.value; | ||
} | ||
var entry = _ref2; | ||
var _entry$contentRect = entry.contentRect, | ||
width = _entry$contentRect.width, | ||
height = _entry$contentRect.height; | ||
_this.setCanvasSize(_this.canvas.interface, width, height, 1.25); | ||
_this.setCanvasSize(_this.canvas.drawing, width, height, 1); | ||
_this.setCanvasSize(_this.canvas.temp, width, height, 1); | ||
_this.setCanvasSize(_this.canvas.grid, width, height, 2); | ||
_this.drawGrid(_this.ctx.grid); | ||
_this.loop({ once: true }); | ||
} | ||
}; | ||
_this.setCanvasSize = function (canvas, width, height) { | ||
var maxDpi = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 4; | ||
var dpi = _this.dpi; | ||
// reduce canvas size for hidpi desktop screens | ||
if (window.innerWidth > 1024) { | ||
dpi = Math.min(_this.dpi, maxDpi); | ||
} | ||
canvas.width = width * dpi; | ||
canvas.height = height * dpi; | ||
canvas.style.width = width; | ||
canvas.style.height = height; | ||
canvas.getContext("2d").scale(dpi, dpi); | ||
}; | ||
_this.getPointerPos = function (e) { | ||
var rect = _this.canvas.interface.getBoundingClientRect(); | ||
// use cursor pos as default | ||
@@ -106,5 +270,5 @@ var clientX = e.clientX; | ||
// use first touch if available | ||
if (e.touches && e.touches.length > 0) { | ||
clientX = e.touches[0].clientX; | ||
clientY = e.touches[0].clientY; | ||
if (e.changedTouches && e.changedTouches.length > 0) { | ||
clientX = e.changedTouches[0].clientX; | ||
clientY = e.changedTouches[0].clientY; | ||
} | ||
@@ -119,140 +283,303 @@ | ||
_this.clear = function () { | ||
if (_this.ctx) { | ||
_this.ctx.clearRect(0, 0, _this.props.canvasWidth, _this.props.canvasHeight); | ||
_this.handlePointerMove = function (x, y) { | ||
if (_this.props.disabled) return; | ||
var hasChanged = _this.lazy.update({ x: x, y: y }); | ||
var isDisabled = !_this.lazy.isEnabled(); | ||
if (_this.isPressing && hasChanged && !_this.isDrawing || isDisabled && _this.isPressing) { | ||
// Start drawing and add point | ||
_this.isDrawing = true; | ||
_this.points.push(_this.lazy.brush.toObject()); | ||
} | ||
_this.timeoutValidity++; | ||
_this.linesArray = []; | ||
_this.startDrawIdx = []; | ||
}; | ||
_this.undo = function () { | ||
if (_this.startDrawIdx.length > 0) { | ||
_this.linesArray.splice(_this.startDrawIdx.pop()); | ||
_this.redraw(true); | ||
return true; | ||
if (_this.isDrawing && (_this.lazy.brushHasMoved() || isDisabled)) { | ||
// Add new point | ||
_this.points.push(_this.lazy.brush.toObject()); | ||
// Draw current points | ||
_this.drawPoints({ | ||
points: _this.points, | ||
brushColor: _this.props.brushColor, | ||
brushRadius: _this.props.brushRadius | ||
}); | ||
} | ||
return false; | ||
_this.mouseHasMoved = true; | ||
}; | ||
_this.drawLine = function (line) { | ||
if (!_this.ctx) return; | ||
_this.drawPoints = function (_ref3) { | ||
var points = _ref3.points, | ||
brushColor = _ref3.brushColor, | ||
brushRadius = _ref3.brushRadius; | ||
_this.ctx.strokeStyle = line.color; | ||
_this.ctx.lineWidth = line.size; | ||
_this.ctx.lineCap = "round"; | ||
_this.ctx.beginPath(); | ||
_this.ctx.moveTo(line.startX, line.startY); | ||
_this.ctx.lineTo(line.endX, line.endY); | ||
_this.ctx.stroke(); | ||
_this.ctx.temp.lineJoin = "round"; | ||
_this.ctx.temp.lineCap = "round"; | ||
_this.ctx.temp.strokeStyle = brushColor; | ||
_this.ctx.temp.clearRect(0, 0, _this.ctx.temp.canvas.width, _this.ctx.temp.canvas.height); | ||
_this.ctx.temp.lineWidth = brushRadius * 2; | ||
var p1 = points[0]; | ||
var p2 = points[1]; | ||
_this.ctx.temp.moveTo(p2.x, p2.y); | ||
_this.ctx.temp.beginPath(); | ||
for (var i = 1, len = points.length; i < len; i++) { | ||
// we pick the point between pi+1 & pi+2 as the | ||
// end point and p1 as our control point | ||
var midPoint = midPointBtw(p1, p2); | ||
_this.ctx.temp.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); | ||
p1 = points[i]; | ||
p2 = points[i + 1]; | ||
} | ||
// Draw last line as a straight line while | ||
// we wait for the next point to be able to calculate | ||
// the bezier control point | ||
_this.ctx.temp.lineTo(p1.x, p1.y); | ||
_this.ctx.temp.stroke(); | ||
}; | ||
_this.drawStart = function (e) { | ||
_this.isMouseDown = true; | ||
_this.startDrawIdx.push(_this.linesArray.length); | ||
_this.saveLine = function () { | ||
var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
brushColor = _ref4.brushColor, | ||
brushRadius = _ref4.brushRadius; | ||
var _this$getMousePos = _this.getMousePos(e), | ||
x = _this$getMousePos.x, | ||
y = _this$getMousePos.y; | ||
// Save as new line | ||
_this.lines.push({ | ||
points: [].concat(_this.points), | ||
brushColor: brushColor || _this.props.brushColor, | ||
brushRadius: brushRadius || _this.props.brushRadius | ||
}); | ||
_this.x = x; | ||
_this.y = y; | ||
// Reset points array | ||
_this.points.length = 0; | ||
// make sure we start painting, useful to draw simple dots | ||
_this.draw(e); | ||
var dpi = window.innerWidth > 1024 ? 1 : window.devicePixelRatio; | ||
var width = _this.canvas.temp.width / dpi; | ||
var height = _this.canvas.temp.height / dpi; | ||
// Copy the line to the drawing canvas | ||
_this.ctx.drawing.drawImage(_this.canvas.temp, 0, 0, width, height); | ||
// Clear the temporary line-drawing canvas | ||
_this.ctx.temp.clearRect(0, 0, width, height); | ||
}; | ||
_this.drawEnd = function () { | ||
_this.isMouseDown = false; | ||
_this.clear = function () { | ||
_this.lines.length = 0; | ||
_this.valuesChanged = true; | ||
_this.ctx.drawing.clearRect(0, 0, _this.canvas.drawing.width, _this.canvas.drawing.height); | ||
_this.ctx.temp.clearRect(0, 0, _this.canvas.temp.width, _this.canvas.temp.height); | ||
}; | ||
_this.draw = function (e) { | ||
if (!_this.isMouseDown || _this.props.disabled) return; | ||
_this.loop = function () { | ||
var _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
_ref5$once = _ref5.once, | ||
once = _ref5$once === undefined ? false : _ref5$once; | ||
// calculate the current x, y coords | ||
if (_this.mouseHasMoved || _this.valuesChanged) { | ||
var pointer = _this.lazy.getPointerCoordinates(); | ||
var brush = _this.lazy.getBrushCoordinates(); | ||
var _this$getMousePos2 = _this.getMousePos(e), | ||
x = _this$getMousePos2.x, | ||
y = _this$getMousePos2.y; | ||
_this.drawInterface(_this.ctx.interface, pointer, brush); | ||
_this.mouseHasMoved = false; | ||
_this.valuesChanged = false; | ||
} | ||
// Offset by 1 to ensure drawing a dot on click | ||
if (!once) { | ||
window.requestAnimationFrame(function () { | ||
_this.loop(); | ||
}); | ||
} | ||
}; | ||
_this.drawGrid = function (ctx) { | ||
if (_this.props.hideGrid) return; | ||
var newX = x + 1; | ||
var newY = y + 1; | ||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); | ||
// create current line object | ||
var line = { | ||
color: _this.props.brushColor, | ||
size: _this.props.brushSize, | ||
startX: _this.x, | ||
startY: _this.y, | ||
endX: newX, | ||
endY: newY | ||
}; | ||
ctx.beginPath(); | ||
ctx.setLineDash([5, 1]); | ||
ctx.setLineDash([]); | ||
ctx.strokeStyle = _this.props.gridColor; | ||
ctx.lineWidth = 0.5; | ||
// actually draw the line | ||
_this.drawLine(line); | ||
var gridSize = 25; | ||
// push it to our array of lines | ||
_this.linesArray.push(line); | ||
var countX = 0; | ||
while (countX < ctx.canvas.width) { | ||
countX += gridSize; | ||
ctx.moveTo(countX, 0); | ||
ctx.lineTo(countX, ctx.canvas.height); | ||
} | ||
ctx.stroke(); | ||
// notify parent that a new line was added | ||
if (typeof _this.props.onChange === "function") { | ||
_this.props.onChange(_this.linesArray); | ||
var countY = 0; | ||
while (countY < ctx.canvas.height) { | ||
countY += gridSize; | ||
ctx.moveTo(0, countY); | ||
ctx.lineTo(ctx.canvas.width, countY); | ||
} | ||
ctx.stroke(); | ||
}; | ||
// set current x, y coords | ||
_this.x = newX; | ||
_this.y = newY; | ||
_this.drawInterface = function (ctx, pointer, brush) { | ||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); | ||
// Draw brush preview | ||
ctx.beginPath(); | ||
ctx.fillStyle = _this.props.brushColor; | ||
ctx.arc(brush.x, brush.y, _this.props.brushRadius, 0, Math.PI * 2, true); | ||
ctx.fill(); | ||
// Draw mouse point (the one directly at the cursor) | ||
ctx.beginPath(); | ||
ctx.fillStyle = _this.props.catenaryColor; | ||
ctx.arc(pointer.x, pointer.y, 4, 0, Math.PI * 2, true); | ||
ctx.fill(); | ||
// Draw catenary | ||
if (_this.lazy.isEnabled()) { | ||
ctx.beginPath(); | ||
ctx.lineWidth = 2; | ||
ctx.lineCap = "round"; | ||
ctx.setLineDash([2, 4]); | ||
ctx.strokeStyle = _this.props.catenaryColor; | ||
_this.catenary.drawToCanvas(_this.ctx.interface, brush, pointer, _this.chainLength); | ||
ctx.stroke(); | ||
} | ||
// Draw brush point (the one in the middle of the brush preview) | ||
ctx.beginPath(); | ||
ctx.fillStyle = _this.props.catenaryColor; | ||
ctx.arc(brush.x, brush.y, 2, 0, Math.PI * 2, true); | ||
ctx.fill(); | ||
}; | ||
_this.isMouseDown = false; | ||
_this.linesArray = []; | ||
_this.startDrawIdx = []; | ||
_this.timeoutValidity = 0; | ||
_this.canvas = {}; | ||
_this.ctx = {}; | ||
_this.catenary = new Catenary(); | ||
_this.lazy = new LazyBrush({ | ||
radius: props.lazyRadius, | ||
enabled: true, | ||
initialPoint: { | ||
x: window.innerWidth / 2, | ||
y: window.innerHeight / 2 | ||
} | ||
}); | ||
_this.points = []; | ||
_this.lines = []; | ||
_this.mouseHasMoved = true; | ||
_this.valuesChanged = true; | ||
_this.isDrawing = false; | ||
_this.isPressing = false; | ||
_this.chainLength = props.lazyRadius; | ||
_this.dpi = 1; | ||
return _this; | ||
} | ||
_default.prototype.render = function render() { | ||
_default.prototype.componentDidMount = function componentDidMount() { | ||
var _this2 = this; | ||
return React.createElement("canvas", { | ||
width: this.props.canvasWidth, | ||
height: this.props.canvasHeight, | ||
style: _extends({ | ||
display: "block", | ||
background: "#fff", | ||
touchAction: "none" | ||
}, this.props.style), | ||
ref: function ref(canvas) { | ||
if (canvas) { | ||
_this2.canvas = canvas; | ||
_this2.ctx = canvas.getContext("2d"); | ||
var observeCanvas = new ResizeObserver(function (entries, observer) { | ||
return _this2.handleCanvasResize(entries, observer); | ||
}); | ||
observeCanvas.observe(this.canvasContainer); | ||
this.drawImage(); | ||
this.loop(); | ||
window.setTimeout(function () { | ||
var initX = window.innerWidth / 2; | ||
var initY = window.innerHeight / 2; | ||
_this2.lazy.update({ x: initX - _this2.chainLength / 4, y: initY }, { both: true }); | ||
_this2.lazy.update({ x: initX + _this2.chainLength / 4, y: initY }, { both: false }); | ||
_this2.mouseHasMoved = true; | ||
_this2.valuesChanged = true; | ||
_this2.clear(); | ||
}, 100); | ||
}; | ||
_default.prototype.componentDidUpdate = function componentDidUpdate(prevProps) { | ||
if (prevProps.lazyRadius !== this.props.lazyRadius) { | ||
// Set new lazyRadius values | ||
this.chainLength = this.props.lazyRadius; | ||
this.lazy.setRadius(this.props.lazyRadius); | ||
} | ||
if (JSON.stringify(prevProps) !== JSON.stringify(this.props)) { | ||
// Signal this.loop function that values changed | ||
this.valuesChanged = true; | ||
} | ||
}; | ||
_default.prototype.render = function render() { | ||
var _this3 = this; | ||
return React.createElement( | ||
"div", | ||
{ | ||
style: _extends({ | ||
display: "block", | ||
background: "#fff", | ||
touchAction: "none", | ||
width: this.props.canvasWidth, | ||
height: this.props.canvasHeight | ||
}, this.props.style), | ||
ref: function ref(container) { | ||
if (container) { | ||
_this3.canvasContainer = container; | ||
} | ||
} | ||
}, | ||
onMouseDown: this.drawStart, | ||
onClick: function onClick() { | ||
return false; | ||
}, | ||
onMouseUp: this.drawEnd, | ||
onMouseOut: this.drawEnd, | ||
onMouseMove: this.draw, | ||
onTouchStart: this.drawStart, | ||
onTouchMove: this.draw, | ||
onTouchEnd: this.drawEnd, | ||
onTouchCancel: this.drawEnd | ||
}); | ||
canvasTypes.map(function (_ref6) { | ||
var name = _ref6.name, | ||
zIndex = _ref6.zIndex; | ||
var isInterface = name === "interface"; | ||
return React.createElement("canvas", { | ||
key: name, | ||
ref: function ref(canvas) { | ||
if (canvas) { | ||
_this3.canvas[name] = canvas; | ||
_this3.ctx[name] = canvas.getContext("2d"); | ||
} | ||
}, | ||
style: _extends({}, canvasStyle, { zIndex: zIndex }), | ||
onMouseDown: isInterface ? _this3.handleMouseDown : undefined, | ||
onMouseMove: isInterface ? _this3.handleMouseMove : undefined, | ||
onMouseUp: isInterface ? _this3.handleMouseUp : undefined, | ||
onMouseOut: isInterface ? _this3.handleMouseUp : undefined, | ||
onTouchStart: isInterface ? _this3.handleTouchStart : undefined, | ||
onTouchMove: isInterface ? _this3.handleTouchMove : undefined, | ||
onTouchEnd: isInterface ? _this3.handleTouchEnd : undefined, | ||
onTouchCancel: isInterface ? _this3.handleTouchEnd : undefined | ||
}); | ||
}) | ||
); | ||
}; | ||
return _default; | ||
}(Component), _class.defaultProps = { | ||
}(PureComponent), _class.defaultProps = { | ||
loadTimeOffset: 5, | ||
brushSize: 6, | ||
lazyRadius: 30, | ||
brushRadius: 12, | ||
brushColor: "#444", | ||
catenaryColor: "#0a0302", | ||
gridColor: "rgba(150,150,150,0.17)", | ||
hideGrid: false, | ||
canvasWidth: 400, | ||
canvasHeight: 400, | ||
disabled: false | ||
disabled: false, | ||
imgSrc: "" | ||
}, _temp); | ||
export { _default as default }; |
641
lib/index.js
@@ -8,2 +8,4 @@ "use strict"; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
var _class, _temp; | ||
@@ -15,2 +17,14 @@ | ||
var _lazyBrush = require("lazy-brush"); | ||
var _catenaryCurve = require("catenary-curve"); | ||
var _resizeObserverPolyfill = require("resize-observer-polyfill"); | ||
var _resizeObserverPolyfill2 = _interopRequireDefault(_resizeObserverPolyfill); | ||
var _drawImage = require("./drawImage"); | ||
var _drawImage2 = _interopRequireDefault(_drawImage); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -24,89 +38,245 @@ | ||
var _default = (_temp = _class = function (_Component) { | ||
_inherits(_default, _Component); | ||
function midPointBtw(p1, p2) { | ||
return { | ||
x: p1.x + (p2.x - p1.x) / 2, | ||
y: p1.y + (p2.y - p1.y) / 2 | ||
}; | ||
} | ||
var canvasStyle = { | ||
display: "block", | ||
position: "absolute" | ||
}; | ||
var canvasTypes = [{ | ||
name: "interface", | ||
zIndex: 15 | ||
}, { | ||
name: "drawing", | ||
zIndex: 11 | ||
}, { | ||
name: "temp", | ||
zIndex: 12 | ||
}, { | ||
name: "grid", | ||
zIndex: 10 | ||
}]; | ||
var _default = (_temp = _class = function (_PureComponent) { | ||
_inherits(_default, _PureComponent); | ||
function _default(props) { | ||
_classCallCheck(this, _default); | ||
var _this = _possibleConstructorReturn(this, _Component.call(this, props)); | ||
var _this = _possibleConstructorReturn(this, _PureComponent.call(this, props)); | ||
_this.drawImage = function () { | ||
if (!_this.props.imgSrc) return; | ||
// Load the image | ||
_this.image = new Image(); | ||
_this.image.src = _this.props.imgSrc; | ||
// Draw the image once loaded | ||
_this.image.onload = function () { | ||
return (0, _drawImage2.default)({ ctx: _this.ctx.grid, img: _this.image }); | ||
}; | ||
}; | ||
_this.undo = function () { | ||
var lines = _this.lines.slice(0, -1); | ||
_this.clear(); | ||
_this.simulateDrawingLines({ lines: lines, immediate: true }); | ||
}; | ||
_this.getSaveData = function () { | ||
// Construct and return the saveData object | ||
var saveData = { | ||
linesArray: _this.linesArray, | ||
lines: [].concat(_this.lines), | ||
width: _this.props.canvasWidth, | ||
height: _this.props.canvasHeight | ||
}; | ||
return JSON.stringify(saveData); | ||
return saveData; | ||
}; | ||
_this.loadSaveData = function (saveData, immediate) { | ||
try { | ||
if (typeof saveData !== "string") { | ||
throw new Error("saveData needs to be a stringified array!"); | ||
} | ||
// parse first to catch any possible errors before clear() | ||
if ((typeof saveData === "undefined" ? "undefined" : _typeof(saveData)) !== "object") { | ||
throw new Error("saveData needs to be of type object!"); | ||
} | ||
var _JSON$parse = JSON.parse(saveData), | ||
linesArray = _JSON$parse.linesArray, | ||
width = _JSON$parse.width, | ||
height = _JSON$parse.height; | ||
var lines = saveData.lines, | ||
width = saveData.width, | ||
height = saveData.height; | ||
if (!linesArray || typeof linesArray.push !== "function") { | ||
throw new Error("linesArray needs to be an array!"); | ||
} | ||
// start the load-process | ||
_this.clear(); | ||
if (!lines || typeof lines.push !== "function") { | ||
throw new Error("saveData.lines needs to be an array!"); | ||
} | ||
if (width === _this.props.canvasWidth && height === _this.props.canvasHeight) { | ||
_this.linesArray = linesArray; | ||
} else { | ||
// we need to rescale the lines based on saved & current dimensions | ||
var scaleX = _this.props.canvasWidth / width; | ||
var scaleY = _this.props.canvasHeight / height; | ||
var scaleAvg = (scaleX + scaleY) / 2; | ||
_this.clear(); | ||
_this.linesArray = linesArray.map(function (line) { | ||
if (width === _this.props.canvasWidth && height === _this.props.canvasHeight) { | ||
_this.simulateDrawingLines({ | ||
lines: lines, | ||
immediate: immediate | ||
}); | ||
} else { | ||
// we need to rescale the lines based on saved & current dimensions | ||
var scaleX = _this.props.canvasWidth / width; | ||
var scaleY = _this.props.canvasHeight / height; | ||
var scaleAvg = (scaleX + scaleY) / 2; | ||
_this.simulateDrawingLines({ | ||
lines: lines.map(function (line) { | ||
return _extends({}, line, { | ||
endX: line.endX * scaleX, | ||
endY: line.endY * scaleY, | ||
startX: line.startX * scaleX, | ||
startY: line.startY * scaleY, | ||
size: line.size * scaleAvg | ||
points: line.points.map(function (p) { | ||
return { | ||
x: p.x * scaleX, | ||
y: p.y * scaleY | ||
}; | ||
}), | ||
brushRadius: line.brushRadius * scaleAvg | ||
}); | ||
}); | ||
} | ||
_this.redraw(immediate); | ||
} catch (err) { | ||
throw err; | ||
}), | ||
immediate: immediate | ||
}); | ||
} | ||
}; | ||
_this.redraw = function (immediate) { | ||
if (_this.ctx) { | ||
_this.ctx.clearRect(0, 0, _this.props.canvasWidth, _this.props.canvasHeight); | ||
} | ||
_this.simulateDrawingLines = function (_ref) { | ||
var lines = _ref.lines, | ||
immediate = _ref.immediate; | ||
_this.timeoutValidity++; | ||
var timeoutValidity = _this.timeoutValidity; | ||
_this.linesArray.forEach(function (line, idx) { | ||
// draw the line with a time offset | ||
// creates the cool drawing-animation effect | ||
if (!immediate) { | ||
// Simulate live-drawing of the loaded lines | ||
var curTime = 0; | ||
var timeoutGap = immediate ? 0 : _this.props.loadTimeOffset; | ||
lines.forEach(function (line) { | ||
var points = line.points, | ||
brushColor = line.brushColor, | ||
brushRadius = line.brushRadius; | ||
var _loop = function _loop(i) { | ||
curTime += timeoutGap; | ||
window.setTimeout(function () { | ||
if (timeoutValidity === _this.timeoutValidity) { | ||
_this.drawLine(line); | ||
} | ||
}, idx * _this.props.loadTimeOffset); | ||
} else { | ||
// if the immediate flag is true, draw without timeout | ||
_this.drawLine(line); | ||
_this.drawPoints({ | ||
points: points.slice(0, i + 1), | ||
brushColor: brushColor, | ||
brushRadius: brushRadius | ||
}); | ||
}, curTime); | ||
}; | ||
for (var i = 1; i < points.length; i++) { | ||
_loop(i); | ||
} | ||
curTime += timeoutGap; | ||
window.setTimeout(function () { | ||
// Save this line with its props instead of this.props | ||
_this.points = points; | ||
_this.saveLine({ brushColor: brushColor, brushRadius: brushRadius }); | ||
}, curTime); | ||
}); | ||
}; | ||
_this.getMousePos = function (e) { | ||
var rect = _this.canvas.getBoundingClientRect(); | ||
_this.handleTouchStart = function (e) { | ||
var _this$getPointerPos = _this.getPointerPos(e), | ||
x = _this$getPointerPos.x, | ||
y = _this$getPointerPos.y; | ||
_this.lazy.update({ x: x, y: y }, { both: true }); | ||
_this.handleMouseDown(e); | ||
_this.mouseHasMoved = true; | ||
}; | ||
_this.handleTouchMove = function (e) { | ||
e.preventDefault(); | ||
var _this$getPointerPos2 = _this.getPointerPos(e), | ||
x = _this$getPointerPos2.x, | ||
y = _this$getPointerPos2.y; | ||
_this.handlePointerMove(x, y); | ||
}; | ||
_this.handleTouchEnd = function (e) { | ||
_this.handleMouseUp(e); | ||
var brush = _this.lazy.getBrushCoordinates(); | ||
_this.lazy.update({ x: brush.x, y: brush.y }, { both: true }); | ||
_this.mouseHasMoved = true; | ||
}; | ||
_this.handleMouseDown = function (e) { | ||
e.preventDefault(); | ||
_this.isPressing = true; | ||
}; | ||
_this.handleMouseMove = function (e) { | ||
var _this$getPointerPos3 = _this.getPointerPos(e), | ||
x = _this$getPointerPos3.x, | ||
y = _this$getPointerPos3.y; | ||
_this.handlePointerMove(x, y); | ||
}; | ||
_this.handleMouseUp = function (e) { | ||
e.preventDefault(); | ||
_this.isDrawing = false; | ||
_this.isPressing = false; | ||
_this.saveLine(); | ||
}; | ||
_this.handleCanvasResize = function (entries, observer) { | ||
_this.dpi = window.devicePixelRatio; | ||
for (var _iterator = entries, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { | ||
var _ref2; | ||
if (_isArray) { | ||
if (_i >= _iterator.length) break; | ||
_ref2 = _iterator[_i++]; | ||
} else { | ||
_i = _iterator.next(); | ||
if (_i.done) break; | ||
_ref2 = _i.value; | ||
} | ||
var entry = _ref2; | ||
var _entry$contentRect = entry.contentRect, | ||
width = _entry$contentRect.width, | ||
height = _entry$contentRect.height; | ||
_this.setCanvasSize(_this.canvas.interface, width, height, 1.25); | ||
_this.setCanvasSize(_this.canvas.drawing, width, height, 1); | ||
_this.setCanvasSize(_this.canvas.temp, width, height, 1); | ||
_this.setCanvasSize(_this.canvas.grid, width, height, 2); | ||
_this.drawGrid(_this.ctx.grid); | ||
_this.loop({ once: true }); | ||
} | ||
}; | ||
_this.setCanvasSize = function (canvas, width, height) { | ||
var maxDpi = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 4; | ||
var dpi = _this.dpi; | ||
// reduce canvas size for hidpi desktop screens | ||
if (window.innerWidth > 1024) { | ||
dpi = Math.min(_this.dpi, maxDpi); | ||
} | ||
canvas.width = width * dpi; | ||
canvas.height = height * dpi; | ||
canvas.style.width = width; | ||
canvas.style.height = height; | ||
canvas.getContext("2d").scale(dpi, dpi); | ||
}; | ||
_this.getPointerPos = function (e) { | ||
var rect = _this.canvas.interface.getBoundingClientRect(); | ||
// use cursor pos as default | ||
@@ -117,5 +287,5 @@ var clientX = e.clientX; | ||
// use first touch if available | ||
if (e.touches && e.touches.length > 0) { | ||
clientX = e.touches[0].clientX; | ||
clientY = e.touches[0].clientY; | ||
if (e.changedTouches && e.changedTouches.length > 0) { | ||
clientX = e.changedTouches[0].clientX; | ||
clientY = e.changedTouches[0].clientY; | ||
} | ||
@@ -130,138 +300,301 @@ | ||
_this.clear = function () { | ||
if (_this.ctx) { | ||
_this.ctx.clearRect(0, 0, _this.props.canvasWidth, _this.props.canvasHeight); | ||
_this.handlePointerMove = function (x, y) { | ||
if (_this.props.disabled) return; | ||
var hasChanged = _this.lazy.update({ x: x, y: y }); | ||
var isDisabled = !_this.lazy.isEnabled(); | ||
if (_this.isPressing && hasChanged && !_this.isDrawing || isDisabled && _this.isPressing) { | ||
// Start drawing and add point | ||
_this.isDrawing = true; | ||
_this.points.push(_this.lazy.brush.toObject()); | ||
} | ||
_this.timeoutValidity++; | ||
_this.linesArray = []; | ||
_this.startDrawIdx = []; | ||
}; | ||
_this.undo = function () { | ||
if (_this.startDrawIdx.length > 0) { | ||
_this.linesArray.splice(_this.startDrawIdx.pop()); | ||
_this.redraw(true); | ||
return true; | ||
if (_this.isDrawing && (_this.lazy.brushHasMoved() || isDisabled)) { | ||
// Add new point | ||
_this.points.push(_this.lazy.brush.toObject()); | ||
// Draw current points | ||
_this.drawPoints({ | ||
points: _this.points, | ||
brushColor: _this.props.brushColor, | ||
brushRadius: _this.props.brushRadius | ||
}); | ||
} | ||
return false; | ||
_this.mouseHasMoved = true; | ||
}; | ||
_this.drawLine = function (line) { | ||
if (!_this.ctx) return; | ||
_this.drawPoints = function (_ref3) { | ||
var points = _ref3.points, | ||
brushColor = _ref3.brushColor, | ||
brushRadius = _ref3.brushRadius; | ||
_this.ctx.strokeStyle = line.color; | ||
_this.ctx.lineWidth = line.size; | ||
_this.ctx.lineCap = "round"; | ||
_this.ctx.beginPath(); | ||
_this.ctx.moveTo(line.startX, line.startY); | ||
_this.ctx.lineTo(line.endX, line.endY); | ||
_this.ctx.stroke(); | ||
_this.ctx.temp.lineJoin = "round"; | ||
_this.ctx.temp.lineCap = "round"; | ||
_this.ctx.temp.strokeStyle = brushColor; | ||
_this.ctx.temp.clearRect(0, 0, _this.ctx.temp.canvas.width, _this.ctx.temp.canvas.height); | ||
_this.ctx.temp.lineWidth = brushRadius * 2; | ||
var p1 = points[0]; | ||
var p2 = points[1]; | ||
_this.ctx.temp.moveTo(p2.x, p2.y); | ||
_this.ctx.temp.beginPath(); | ||
for (var i = 1, len = points.length; i < len; i++) { | ||
// we pick the point between pi+1 & pi+2 as the | ||
// end point and p1 as our control point | ||
var midPoint = midPointBtw(p1, p2); | ||
_this.ctx.temp.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); | ||
p1 = points[i]; | ||
p2 = points[i + 1]; | ||
} | ||
// Draw last line as a straight line while | ||
// we wait for the next point to be able to calculate | ||
// the bezier control point | ||
_this.ctx.temp.lineTo(p1.x, p1.y); | ||
_this.ctx.temp.stroke(); | ||
}; | ||
_this.drawStart = function (e) { | ||
_this.isMouseDown = true; | ||
_this.startDrawIdx.push(_this.linesArray.length); | ||
_this.saveLine = function () { | ||
var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
brushColor = _ref4.brushColor, | ||
brushRadius = _ref4.brushRadius; | ||
var _this$getMousePos = _this.getMousePos(e), | ||
x = _this$getMousePos.x, | ||
y = _this$getMousePos.y; | ||
// Save as new line | ||
_this.lines.push({ | ||
points: [].concat(_this.points), | ||
brushColor: brushColor || _this.props.brushColor, | ||
brushRadius: brushRadius || _this.props.brushRadius | ||
}); | ||
_this.x = x; | ||
_this.y = y; | ||
// Reset points array | ||
_this.points.length = 0; | ||
// make sure we start painting, useful to draw simple dots | ||
_this.draw(e); | ||
var dpi = window.innerWidth > 1024 ? 1 : window.devicePixelRatio; | ||
var width = _this.canvas.temp.width / dpi; | ||
var height = _this.canvas.temp.height / dpi; | ||
// Copy the line to the drawing canvas | ||
_this.ctx.drawing.drawImage(_this.canvas.temp, 0, 0, width, height); | ||
// Clear the temporary line-drawing canvas | ||
_this.ctx.temp.clearRect(0, 0, width, height); | ||
}; | ||
_this.drawEnd = function () { | ||
_this.isMouseDown = false; | ||
_this.clear = function () { | ||
_this.lines.length = 0; | ||
_this.valuesChanged = true; | ||
_this.ctx.drawing.clearRect(0, 0, _this.canvas.drawing.width, _this.canvas.drawing.height); | ||
_this.ctx.temp.clearRect(0, 0, _this.canvas.temp.width, _this.canvas.temp.height); | ||
}; | ||
_this.draw = function (e) { | ||
if (!_this.isMouseDown || _this.props.disabled) return; | ||
_this.loop = function () { | ||
var _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
_ref5$once = _ref5.once, | ||
once = _ref5$once === undefined ? false : _ref5$once; | ||
// calculate the current x, y coords | ||
if (_this.mouseHasMoved || _this.valuesChanged) { | ||
var pointer = _this.lazy.getPointerCoordinates(); | ||
var brush = _this.lazy.getBrushCoordinates(); | ||
var _this$getMousePos2 = _this.getMousePos(e), | ||
x = _this$getMousePos2.x, | ||
y = _this$getMousePos2.y; | ||
_this.drawInterface(_this.ctx.interface, pointer, brush); | ||
_this.mouseHasMoved = false; | ||
_this.valuesChanged = false; | ||
} | ||
// Offset by 1 to ensure drawing a dot on click | ||
if (!once) { | ||
window.requestAnimationFrame(function () { | ||
_this.loop(); | ||
}); | ||
} | ||
}; | ||
_this.drawGrid = function (ctx) { | ||
if (_this.props.hideGrid) return; | ||
var newX = x + 1; | ||
var newY = y + 1; | ||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); | ||
// create current line object | ||
var line = { | ||
color: _this.props.brushColor, | ||
size: _this.props.brushSize, | ||
startX: _this.x, | ||
startY: _this.y, | ||
endX: newX, | ||
endY: newY | ||
}; | ||
ctx.beginPath(); | ||
ctx.setLineDash([5, 1]); | ||
ctx.setLineDash([]); | ||
ctx.strokeStyle = _this.props.gridColor; | ||
ctx.lineWidth = 0.5; | ||
// actually draw the line | ||
_this.drawLine(line); | ||
var gridSize = 25; | ||
// push it to our array of lines | ||
_this.linesArray.push(line); | ||
var countX = 0; | ||
while (countX < ctx.canvas.width) { | ||
countX += gridSize; | ||
ctx.moveTo(countX, 0); | ||
ctx.lineTo(countX, ctx.canvas.height); | ||
} | ||
ctx.stroke(); | ||
// notify parent that a new line was added | ||
if (typeof _this.props.onChange === "function") { | ||
_this.props.onChange(_this.linesArray); | ||
var countY = 0; | ||
while (countY < ctx.canvas.height) { | ||
countY += gridSize; | ||
ctx.moveTo(0, countY); | ||
ctx.lineTo(ctx.canvas.width, countY); | ||
} | ||
ctx.stroke(); | ||
}; | ||
// set current x, y coords | ||
_this.x = newX; | ||
_this.y = newY; | ||
_this.drawInterface = function (ctx, pointer, brush) { | ||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); | ||
// Draw brush preview | ||
ctx.beginPath(); | ||
ctx.fillStyle = _this.props.brushColor; | ||
ctx.arc(brush.x, brush.y, _this.props.brushRadius, 0, Math.PI * 2, true); | ||
ctx.fill(); | ||
// Draw mouse point (the one directly at the cursor) | ||
ctx.beginPath(); | ||
ctx.fillStyle = _this.props.catenaryColor; | ||
ctx.arc(pointer.x, pointer.y, 4, 0, Math.PI * 2, true); | ||
ctx.fill(); | ||
// Draw catenary | ||
if (_this.lazy.isEnabled()) { | ||
ctx.beginPath(); | ||
ctx.lineWidth = 2; | ||
ctx.lineCap = "round"; | ||
ctx.setLineDash([2, 4]); | ||
ctx.strokeStyle = _this.props.catenaryColor; | ||
_this.catenary.drawToCanvas(_this.ctx.interface, brush, pointer, _this.chainLength); | ||
ctx.stroke(); | ||
} | ||
// Draw brush point (the one in the middle of the brush preview) | ||
ctx.beginPath(); | ||
ctx.fillStyle = _this.props.catenaryColor; | ||
ctx.arc(brush.x, brush.y, 2, 0, Math.PI * 2, true); | ||
ctx.fill(); | ||
}; | ||
_this.isMouseDown = false; | ||
_this.linesArray = []; | ||
_this.startDrawIdx = []; | ||
_this.timeoutValidity = 0; | ||
_this.canvas = {}; | ||
_this.ctx = {}; | ||
_this.catenary = new _catenaryCurve.Catenary(); | ||
_this.lazy = new _lazyBrush.LazyBrush({ | ||
radius: props.lazyRadius, | ||
enabled: true, | ||
initialPoint: { | ||
x: window.innerWidth / 2, | ||
y: window.innerHeight / 2 | ||
} | ||
}); | ||
_this.points = []; | ||
_this.lines = []; | ||
_this.mouseHasMoved = true; | ||
_this.valuesChanged = true; | ||
_this.isDrawing = false; | ||
_this.isPressing = false; | ||
_this.chainLength = props.lazyRadius; | ||
_this.dpi = 1; | ||
return _this; | ||
} | ||
_default.prototype.render = function render() { | ||
_default.prototype.componentDidMount = function componentDidMount() { | ||
var _this2 = this; | ||
return _react2.default.createElement("canvas", { | ||
width: this.props.canvasWidth, | ||
height: this.props.canvasHeight, | ||
style: _extends({ | ||
display: "block", | ||
background: "#fff", | ||
touchAction: "none" | ||
}, this.props.style), | ||
ref: function ref(canvas) { | ||
if (canvas) { | ||
_this2.canvas = canvas; | ||
_this2.ctx = canvas.getContext("2d"); | ||
var observeCanvas = new _resizeObserverPolyfill2.default(function (entries, observer) { | ||
return _this2.handleCanvasResize(entries, observer); | ||
}); | ||
observeCanvas.observe(this.canvasContainer); | ||
this.drawImage(); | ||
this.loop(); | ||
window.setTimeout(function () { | ||
var initX = window.innerWidth / 2; | ||
var initY = window.innerHeight / 2; | ||
_this2.lazy.update({ x: initX - _this2.chainLength / 4, y: initY }, { both: true }); | ||
_this2.lazy.update({ x: initX + _this2.chainLength / 4, y: initY }, { both: false }); | ||
_this2.mouseHasMoved = true; | ||
_this2.valuesChanged = true; | ||
_this2.clear(); | ||
}, 100); | ||
}; | ||
_default.prototype.componentDidUpdate = function componentDidUpdate(prevProps) { | ||
if (prevProps.lazyRadius !== this.props.lazyRadius) { | ||
// Set new lazyRadius values | ||
this.chainLength = this.props.lazyRadius; | ||
this.lazy.setRadius(this.props.lazyRadius); | ||
} | ||
if (JSON.stringify(prevProps) !== JSON.stringify(this.props)) { | ||
// Signal this.loop function that values changed | ||
this.valuesChanged = true; | ||
} | ||
}; | ||
_default.prototype.render = function render() { | ||
var _this3 = this; | ||
return _react2.default.createElement( | ||
"div", | ||
{ | ||
style: _extends({ | ||
display: "block", | ||
background: "#fff", | ||
touchAction: "none", | ||
width: this.props.canvasWidth, | ||
height: this.props.canvasHeight | ||
}, this.props.style), | ||
ref: function ref(container) { | ||
if (container) { | ||
_this3.canvasContainer = container; | ||
} | ||
} | ||
}, | ||
onMouseDown: this.drawStart, | ||
onClick: function onClick() { | ||
return false; | ||
}, | ||
onMouseUp: this.drawEnd, | ||
onMouseOut: this.drawEnd, | ||
onMouseMove: this.draw, | ||
onTouchStart: this.drawStart, | ||
onTouchMove: this.draw, | ||
onTouchEnd: this.drawEnd, | ||
onTouchCancel: this.drawEnd | ||
}); | ||
canvasTypes.map(function (_ref6) { | ||
var name = _ref6.name, | ||
zIndex = _ref6.zIndex; | ||
var isInterface = name === "interface"; | ||
return _react2.default.createElement("canvas", { | ||
key: name, | ||
ref: function ref(canvas) { | ||
if (canvas) { | ||
_this3.canvas[name] = canvas; | ||
_this3.ctx[name] = canvas.getContext("2d"); | ||
} | ||
}, | ||
style: _extends({}, canvasStyle, { zIndex: zIndex }), | ||
onMouseDown: isInterface ? _this3.handleMouseDown : undefined, | ||
onMouseMove: isInterface ? _this3.handleMouseMove : undefined, | ||
onMouseUp: isInterface ? _this3.handleMouseUp : undefined, | ||
onMouseOut: isInterface ? _this3.handleMouseUp : undefined, | ||
onTouchStart: isInterface ? _this3.handleTouchStart : undefined, | ||
onTouchMove: isInterface ? _this3.handleTouchMove : undefined, | ||
onTouchEnd: isInterface ? _this3.handleTouchEnd : undefined, | ||
onTouchCancel: isInterface ? _this3.handleTouchEnd : undefined | ||
}); | ||
}) | ||
); | ||
}; | ||
return _default; | ||
}(_react.Component), _class.defaultProps = { | ||
}(_react.PureComponent), _class.defaultProps = { | ||
loadTimeOffset: 5, | ||
brushSize: 6, | ||
lazyRadius: 30, | ||
brushRadius: 12, | ||
brushColor: "#444", | ||
catenaryColor: "#0a0302", | ||
gridColor: "rgba(150,150,150,0.17)", | ||
hideGrid: false, | ||
canvasWidth: 400, | ||
canvasHeight: 400, | ||
disabled: false | ||
disabled: false, | ||
imgSrc: "" | ||
}, _temp); | ||
@@ -268,0 +601,0 @@ |
{ | ||
"name": "react-canvas-draw", | ||
"version": "0.1.9", | ||
"version": "1.0.0-alpha.1", | ||
"description": "A simple yet powerful canvas-drawing component for React.", | ||
@@ -22,3 +22,7 @@ "main": "lib/index.js", | ||
}, | ||
"dependencies": {}, | ||
"dependencies": { | ||
"catenary-curve": "^1.0.1", | ||
"lazy-brush": "^1.0.1", | ||
"resize-observer-polyfill": "^1.5.0" | ||
}, | ||
"peerDependencies": { | ||
@@ -25,0 +29,0 @@ "react": "16.x" |
# React Canvas Draw | ||
[![Travis][build-badge]][build] | ||
[![npm package][npm-badge]][npm] | ||
[![Coveralls][coveralls-badge]][coveralls] | ||
@@ -23,7 +25,5 @@ | ||
No additional dependencies needed. | ||
## Usage | ||
``` | ||
```javascript | ||
import React from "react"; | ||
@@ -33,6 +33,3 @@ import ReactDOM from "react-dom"; | ||
ReactDOM.render( | ||
<CanvasDraw />, | ||
document.getElementById('root') | ||
); | ||
ReactDOM.render(<CanvasDraw />, document.getElementById("root")); | ||
``` | ||
@@ -42,16 +39,21 @@ | ||
Even more examples are coming, check back soon! | ||
### Props | ||
These are the defaultProps of CanvasDraw. You can pass along any of these props to customize the CanvasDraw component. Examples of how to use the props are also shown in the [`/demo/src` folder](https://github.com/mBeierl/react-canvas-draw/tree/master/demo/src). | ||
```javascript | ||
static defaultProps = { | ||
loadTimeOffset: 5, | ||
lazyRadius: 30, | ||
brushRadius: 12, | ||
brushColor: "#444", | ||
catenaryColor: "#0a0302", | ||
gridColor: "rgba(150,150,150,0.17)", | ||
hideGrid: false, | ||
canvasWidth: 400, | ||
canvasHeight: 400, | ||
disabled: false, | ||
imgSrc: "" | ||
}; | ||
``` | ||
static defaultProps = { | ||
loadTimeOffset: 5, | ||
brushSize: 6, | ||
brushColor: "#444", | ||
canvasWidth: 400, | ||
canvasHeight: 400, | ||
disabled: false | ||
}; | ||
``` | ||
@@ -62,19 +64,7 @@ ### Functions | ||
* `getSaveData()` returns the drawing's save-data as stringified JSON | ||
* `loadSaveData(saveData: String, immediate: Boolean)` loads a previously saved drawing using the saveData string, as well as an optional boolean flag to load it immediately, instead of live-drawing it. | ||
* `clear()` clears the canvas completely | ||
* `undo()` removes the latest change to the drawing. This includes everything drawn since the last MouseDown event. | ||
* `drawLine(line)` to draw a line. This can be useful if you want to automate drawing. The line parameter is an object of the following form: | ||
- `getSaveData()` returns the drawing's save-data as stringified JSON | ||
- `loadSaveData(saveData: Object, immediate: Boolean)` loads a previously saved drawing using the saveData string, as well as an optional boolean flag to load it immediately, instead of live-drawing it. | ||
- `clear()` clears the canvas completely | ||
- `undo()` removes the latest change to the drawing. This includes everything drawn since the last MouseDown event. | ||
``` | ||
const line = { | ||
color: this.props.brushColor, | ||
size: this.props.brushSize, | ||
startX: this.x, | ||
startY: this.y, | ||
endX: newX, | ||
endY: newY | ||
}; | ||
``` | ||
## Local Development | ||
@@ -90,2 +80,8 @@ | ||
## Acknowledgement | ||
The [lazy-brush](https://github.com/dulnan/lazy-brush) project as well as its demo app by [dulnan](https://github.com/dulnan) have been a heavy influence. | ||
I borrowed a lot of the logic and actually used lazy-brush during the push to v1 of react-canvas-draw. Without it, react-canvas-draw would most likely still be pre v1 and wouldn't feel as good. | ||
## License | ||
@@ -92,0 +88,0 @@ |
/*! | ||
* react-canvas-draw v0.1.9 - https://mbeierl.github.io/react-canvas-draw/ | ||
* react-canvas-draw v1.0.0-alpha.1 - https://mbeierl.github.io/react-canvas-draw/ | ||
* MIT Licensed | ||
*/ | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react")):"function"==typeof define&&define.amd?define(["react"],e):"object"==typeof exports?exports.ReactCanvasDraw=e(require("react")):t.ReactCanvasDraw=e(t.React)}("undefined"!=typeof self?self:this,function(t){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:n})},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,r){t.exports=r(1)},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),r.d(e,"default",function(){return c});var n,o,a=r(2),i=r.n(a),s=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(t[n]=r[n])}return t};var c=(o=n=function(t){function e(r){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e);var n=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,t.call(this,r));return n.getSaveData=function(){var t={linesArray:n.linesArray,width:n.props.canvasWidth,height:n.props.canvasHeight};return JSON.stringify(t)},n.loadSaveData=function(t,e){try{if("string"!=typeof t)throw new Error("saveData needs to be a stringified array!");var r=JSON.parse(t),o=r.linesArray,a=r.width,i=r.height;if(!o||"function"!=typeof o.push)throw new Error("linesArray needs to be an array!");if(n.clear(),a===n.props.canvasWidth&&i===n.props.canvasHeight)n.linesArray=o;else{var c=n.props.canvasWidth/a,u=n.props.canvasHeight/i,p=(c+u)/2;n.linesArray=o.map(function(t){return s({},t,{endX:t.endX*c,endY:t.endY*u,startX:t.startX*c,startY:t.startY*u,size:t.size*p})})}n.redraw(e)}catch(t){throw t}},n.redraw=function(t){n.ctx&&n.ctx.clearRect(0,0,n.props.canvasWidth,n.props.canvasHeight),n.timeoutValidity++;var e=n.timeoutValidity;n.linesArray.forEach(function(r,o){t?n.drawLine(r):window.setTimeout(function(){e===n.timeoutValidity&&n.drawLine(r)},o*n.props.loadTimeOffset)})},n.getMousePos=function(t){var e=n.canvas.getBoundingClientRect(),r=t.clientX,o=t.clientY;return t.touches&&t.touches.length>0&&(r=t.touches[0].clientX,o=t.touches[0].clientY),{x:r-e.left,y:o-e.top}},n.clear=function(){n.ctx&&n.ctx.clearRect(0,0,n.props.canvasWidth,n.props.canvasHeight),n.timeoutValidity++,n.linesArray=[],n.startDrawIdx=[]},n.undo=function(){return n.startDrawIdx.length>0&&(n.linesArray.splice(n.startDrawIdx.pop()),n.redraw(!0),!0)},n.drawLine=function(t){n.ctx&&(n.ctx.strokeStyle=t.color,n.ctx.lineWidth=t.size,n.ctx.lineCap="round",n.ctx.beginPath(),n.ctx.moveTo(t.startX,t.startY),n.ctx.lineTo(t.endX,t.endY),n.ctx.stroke())},n.drawStart=function(t){n.isMouseDown=!0,n.startDrawIdx.push(n.linesArray.length);var e=n.getMousePos(t),r=e.x,o=e.y;n.x=r,n.y=o,n.draw(t)},n.drawEnd=function(){n.isMouseDown=!1},n.draw=function(t){if(n.isMouseDown&&!n.props.disabled){var e=n.getMousePos(t),r=e.x+1,o=e.y+1,a={color:n.props.brushColor,size:n.props.brushSize,startX:n.x,startY:n.y,endX:r,endY:o};n.drawLine(a),n.linesArray.push(a),"function"==typeof n.props.onChange&&n.props.onChange(n.linesArray),n.x=r,n.y=o}},n.isMouseDown=!1,n.linesArray=[],n.startDrawIdx=[],n.timeoutValidity=0,n}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype.render=function(){var t=this;return i.a.createElement("canvas",{width:this.props.canvasWidth,height:this.props.canvasHeight,style:s({display:"block",background:"#fff",touchAction:"none"},this.props.style),ref:function(e){e&&(t.canvas=e,t.ctx=e.getContext("2d"))},onMouseDown:this.drawStart,onClick:function(){return!1},onMouseUp:this.drawEnd,onMouseOut:this.drawEnd,onMouseMove:this.draw,onTouchStart:this.drawStart,onTouchMove:this.draw,onTouchEnd:this.drawEnd,onTouchCancel:this.drawEnd})},e}(a.Component),n.defaultProps={loadTimeOffset:5,brushSize:6,brushColor:"#444",canvasWidth:400,canvasHeight:400,disabled:!1},o)},function(e,r){e.exports=t}]).default}); | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react")):"function"==typeof define&&define.amd?define(["react"],t):"object"==typeof exports?exports.ReactCanvasDraw=t(require("react")):e.ReactCanvasDraw=t(e.React)}("undefined"!=typeof self?self:this,function(e){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var r=t[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:i})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=3)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,r=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}(),o=n(1),a=(i=o)&&i.__esModule?i:{default:i};var s=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,a.default),r(t,[{key:"update",value:function(e){this.x=e.x,this.y=e.y}},{key:"moveByAngle",value:function(e,t){var n=e+Math.PI/2;this.x=this.x+Math.sin(n)*t,this.y=this.y-Math.cos(n)*t}},{key:"equalsTo",value:function(e){return this.x===e.x&&this.y===e.y}},{key:"getDifferenceTo",value:function(e){return new a.default(this.x-e.x,this.y-e.y)}},{key:"getDistanceTo",value:function(e){var t=this.getDifferenceTo(e);return Math.sqrt(Math.pow(t.x,2)+Math.pow(t.y,2))}},{key:"getAngleTo",value:function(e){var t=this.getDifferenceTo(e);return Math.atan2(t.y,t.x)}},{key:"toObject",value:function(){return{x:this.x,y:this.y}}}]),t}();t.default=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.default=function e(t,n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.x=t,this.y=n}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}();var r=function(){function e(t,n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.x=t,this.y=n}return i(e,[{key:"update",value:function(e){this.x=e.x,this.y=e.y}},{key:"getDifferenceTo",value:function(t){return new e(this.x-t.x,this.y-t.y)}},{key:"getDistanceTo",value:function(e){var t=this.getDifferenceTo(e);return Math.sqrt(Math.pow(t.x,2)+Math.pow(t.y,2))}}]),e}();t.default=r},function(e,t,n){e.exports=n(4)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n.d(t,"default",function(){return y});var i,r,o=n(5),a=n.n(o),s=n(6),u=(n.n(s),n(8)),c=(n.n(u),n(10)),h=n(12),d=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function l(e,t){return{x:e.x+(t.x-e.x)/2,y:e.y+(t.y-e.y)/2}}var p={display:"block",position:"absolute"},v=[{name:"interface",zIndex:15},{name:"drawing",zIndex:11},{name:"temp",zIndex:12},{name:"grid",zIndex:10}],y=(r=i=function(e){function t(n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var i=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,e.call(this,n));return i.drawImage=function(){i.props.imgSrc&&(i.image=new Image,i.image.src=i.props.imgSrc,i.image.onload=function(){return Object(h.a)({ctx:i.ctx.grid,img:i.image})})},i.undo=function(){var e=i.lines.slice(0,-1);i.clear(),i.simulateDrawingLines({lines:e,immediate:!0})},i.getSaveData=function(){return{lines:[].concat(i.lines),width:i.props.canvasWidth,height:i.props.canvasHeight}},i.loadSaveData=function(e,t){if("object"!==(void 0===e?"undefined":f(e)))throw new Error("saveData needs to be of type object!");var n=e.lines,r=e.width,o=e.height;if(!n||"function"!=typeof n.push)throw new Error("saveData.lines needs to be an array!");if(i.clear(),r===i.props.canvasWidth&&o===i.props.canvasHeight)i.simulateDrawingLines({lines:n,immediate:t});else{var a=i.props.canvasWidth/r,s=i.props.canvasHeight/o,u=(a+s)/2;i.simulateDrawingLines({lines:n.map(function(e){return d({},e,{points:e.points.map(function(e){return{x:e.x*a,y:e.y*s}}),brushRadius:e.brushRadius*u})}),immediate:t})}},i.simulateDrawingLines=function(e){var t=e.lines,n=0,r=e.immediate?0:i.props.loadTimeOffset;t.forEach(function(e){for(var t=e.points,o=e.brushColor,a=e.brushRadius,s=function(e){n+=r,window.setTimeout(function(){i.drawPoints({points:t.slice(0,e+1),brushColor:o,brushRadius:a})},n)},u=1;u<t.length;u++)s(u);n+=r,window.setTimeout(function(){i.points=t,i.saveLine({brushColor:o,brushRadius:a})},n)})},i.handleTouchStart=function(e){var t=i.getPointerPos(e),n=t.x,r=t.y;i.lazy.update({x:n,y:r},{both:!0}),i.handleMouseDown(e),i.mouseHasMoved=!0},i.handleTouchMove=function(e){e.preventDefault();var t=i.getPointerPos(e),n=t.x,r=t.y;i.handlePointerMove(n,r)},i.handleTouchEnd=function(e){i.handleMouseUp(e);var t=i.lazy.getBrushCoordinates();i.lazy.update({x:t.x,y:t.y},{both:!0}),i.mouseHasMoved=!0},i.handleMouseDown=function(e){e.preventDefault(),i.isPressing=!0},i.handleMouseMove=function(e){var t=i.getPointerPos(e),n=t.x,r=t.y;i.handlePointerMove(n,r)},i.handleMouseUp=function(e){e.preventDefault(),i.isDrawing=!1,i.isPressing=!1,i.saveLine()},i.handleCanvasResize=function(e,t){i.dpi=window.devicePixelRatio;var n=e,r=Array.isArray(n),o=0;for(n=r?n:n[Symbol.iterator]();;){var a;if(r){if(o>=n.length)break;a=n[o++]}else{if((o=n.next()).done)break;a=o.value}var s=a.contentRect,u=s.width,c=s.height;i.setCanvasSize(i.canvas.interface,u,c,1.25),i.setCanvasSize(i.canvas.drawing,u,c,1),i.setCanvasSize(i.canvas.temp,u,c,1),i.setCanvasSize(i.canvas.grid,u,c,2),i.drawGrid(i.ctx.grid),i.loop({once:!0})}},i.setCanvasSize=function(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:4,o=i.dpi;window.innerWidth>1024&&(o=Math.min(i.dpi,r)),e.width=t*o,e.height=n*o,e.style.width=t,e.style.height=n,e.getContext("2d").scale(o,o)},i.getPointerPos=function(e){var t=i.canvas.interface.getBoundingClientRect(),n=e.clientX,r=e.clientY;return e.changedTouches&&e.changedTouches.length>0&&(n=e.changedTouches[0].clientX,r=e.changedTouches[0].clientY),{x:n-t.left,y:r-t.top}},i.handlePointerMove=function(e,t){if(!i.props.disabled){var n=i.lazy.update({x:e,y:t}),r=!i.lazy.isEnabled();(i.isPressing&&n&&!i.isDrawing||r&&i.isPressing)&&(i.isDrawing=!0,i.points.push(i.lazy.brush.toObject())),i.isDrawing&&(i.lazy.brushHasMoved()||r)&&(i.points.push(i.lazy.brush.toObject()),i.drawPoints({points:i.points,brushColor:i.props.brushColor,brushRadius:i.props.brushRadius})),i.mouseHasMoved=!0}},i.drawPoints=function(e){var t=e.points,n=e.brushColor,r=e.brushRadius;i.ctx.temp.lineJoin="round",i.ctx.temp.lineCap="round",i.ctx.temp.strokeStyle=n,i.ctx.temp.clearRect(0,0,i.ctx.temp.canvas.width,i.ctx.temp.canvas.height),i.ctx.temp.lineWidth=2*r;var o=t[0],a=t[1];i.ctx.temp.moveTo(a.x,a.y),i.ctx.temp.beginPath();for(var s=1,u=t.length;s<u;s++){var c=l(o,a);i.ctx.temp.quadraticCurveTo(o.x,o.y,c.x,c.y),o=t[s],a=t[s+1]}i.ctx.temp.lineTo(o.x,o.y),i.ctx.temp.stroke()},i.saveLine=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.brushColor,n=e.brushRadius;i.lines.push({points:[].concat(i.points),brushColor:t||i.props.brushColor,brushRadius:n||i.props.brushRadius}),i.points.length=0;var r=window.innerWidth>1024?1:window.devicePixelRatio,o=i.canvas.temp.width/r,a=i.canvas.temp.height/r;i.ctx.drawing.drawImage(i.canvas.temp,0,0,o,a),i.ctx.temp.clearRect(0,0,o,a)},i.clear=function(){i.lines.length=0,i.valuesChanged=!0,i.ctx.drawing.clearRect(0,0,i.canvas.drawing.width,i.canvas.drawing.height),i.ctx.temp.clearRect(0,0,i.canvas.temp.width,i.canvas.temp.height)},i.loop=function(){var e=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).once,t=void 0!==e&&e;if(i.mouseHasMoved||i.valuesChanged){var n=i.lazy.getPointerCoordinates(),r=i.lazy.getBrushCoordinates();i.drawInterface(i.ctx.interface,n,r),i.mouseHasMoved=!1,i.valuesChanged=!1}t||window.requestAnimationFrame(function(){i.loop()})},i.drawGrid=function(e){if(!i.props.hideGrid){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.beginPath(),e.setLineDash([5,1]),e.setLineDash([]),e.strokeStyle=i.props.gridColor,e.lineWidth=.5;for(var t=0;t<e.canvas.width;)t+=25,e.moveTo(t,0),e.lineTo(t,e.canvas.height);e.stroke();for(var n=0;n<e.canvas.height;)n+=25,e.moveTo(0,n),e.lineTo(e.canvas.width,n);e.stroke()}},i.drawInterface=function(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.beginPath(),e.fillStyle=i.props.brushColor,e.arc(n.x,n.y,i.props.brushRadius,0,2*Math.PI,!0),e.fill(),e.beginPath(),e.fillStyle=i.props.catenaryColor,e.arc(t.x,t.y,4,0,2*Math.PI,!0),e.fill(),i.lazy.isEnabled()&&(e.beginPath(),e.lineWidth=2,e.lineCap="round",e.setLineDash([2,4]),e.strokeStyle=i.props.catenaryColor,i.catenary.drawToCanvas(i.ctx.interface,n,t,i.chainLength),e.stroke()),e.beginPath(),e.fillStyle=i.props.catenaryColor,e.arc(n.x,n.y,2,0,2*Math.PI,!0),e.fill()},i.canvas={},i.ctx={},i.catenary=new u.Catenary,i.lazy=new s.LazyBrush({radius:n.lazyRadius,enabled:!0,initialPoint:{x:window.innerWidth/2,y:window.innerHeight/2}}),i.points=[],i.lines=[],i.mouseHasMoved=!0,i.valuesChanged=!0,i.isDrawing=!1,i.isPressing=!1,i.chainLength=n.lazyRadius,i.dpi=1,i}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),t.prototype.componentDidMount=function(){var e=this;new c.a(function(t,n){return e.handleCanvasResize(t,n)}).observe(this.canvasContainer),this.drawImage(),this.loop(),window.setTimeout(function(){var t=window.innerWidth/2,n=window.innerHeight/2;e.lazy.update({x:t-e.chainLength/4,y:n},{both:!0}),e.lazy.update({x:t+e.chainLength/4,y:n},{both:!1}),e.mouseHasMoved=!0,e.valuesChanged=!0,e.clear()},100)},t.prototype.componentDidUpdate=function(e){e.lazyRadius!==this.props.lazyRadius&&(this.chainLength=this.props.lazyRadius,this.lazy.setRadius(this.props.lazyRadius)),JSON.stringify(e)!==JSON.stringify(this.props)&&(this.valuesChanged=!0)},t.prototype.render=function(){var e=this;return a.a.createElement("div",{style:d({display:"block",background:"#fff",touchAction:"none",width:this.props.canvasWidth,height:this.props.canvasHeight},this.props.style),ref:function(t){t&&(e.canvasContainer=t)}},v.map(function(t){var n=t.name,i=t.zIndex,r="interface"===n;return a.a.createElement("canvas",{key:n,ref:function(t){t&&(e.canvas[n]=t,e.ctx[n]=t.getContext("2d"))},style:d({},p,{zIndex:i}),onMouseDown:r?e.handleMouseDown:void 0,onMouseMove:r?e.handleMouseMove:void 0,onMouseUp:r?e.handleMouseUp:void 0,onMouseOut:r?e.handleMouseUp:void 0,onTouchStart:r?e.handleTouchStart:void 0,onTouchMove:r?e.handleTouchMove:void 0,onTouchEnd:r?e.handleTouchEnd:void 0,onTouchCancel:r?e.handleTouchEnd:void 0})}))},t}(o.PureComponent),i.defaultProps={loadTimeOffset:5,lazyRadius:30,brushRadius:12,brushColor:"#444",catenaryColor:"#0a0302",gridColor:"rgba(150,150,150,0.17)",hideGrid:!1,canvasWidth:400,canvasHeight:400,disabled:!1,imgSrc:""},r)},function(t,n){t.exports=e},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.LazyPoint=t.Point=t.LazyBrush=void 0;var i=a(n(7)),r=a(n(1)),o=a(n(0));function a(e){return e&&e.__esModule?e:{default:e}}t.LazyBrush=i.default,t.Point=r.default,t.LazyPoint=o.default},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,r=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}(),o=n(0),a=(i=o)&&i.__esModule?i:{default:i};var s=30,u=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.radius,i=void 0===n?s:n,r=t.enabled,o=void 0===r||r,u=t.initialPoint,c=void 0===u?{x:0,y:0}:u;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.radius=i,this._isEnabled=o,this.pointer=new a.default(c.x,c.y),this.brush=new a.default(c.x,c.y),this.angle=0,this.distance=0,this._hasMoved=!1}return r(e,[{key:"enable",value:function(){this._isEnabled=!0}},{key:"disable",value:function(){this._isEnabled=!1}},{key:"isEnabled",value:function(){return this._isEnabled}},{key:"setRadius",value:function(e){this.radius=e}},{key:"getRadius",value:function(){return this.radius}},{key:"getBrushCoordinates",value:function(){return this.brush.toObject()}},{key:"getPointerCoordinates",value:function(){return this.pointer.toObject()}},{key:"getBrush",value:function(){return this.brush}},{key:"getPointer",value:function(){return this.pointer}},{key:"getAngle",value:function(){return this.angle}},{key:"getDistance",value:function(){return this.distance}},{key:"brushHasMoved",value:function(){return this._hasMoved}},{key:"update",value:function(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).both,n=void 0!==t&&t;return this._hasMoved=!1,!(this.pointer.equalsTo(e)&&!n)&&(this.pointer.update(e),n?(this._hasMoved=!0,this.brush.update(e),!0):(this._isEnabled?(this.distance=this.pointer.getDistanceTo(this.brush),this.angle=this.pointer.getAngleTo(this.brush),this.distance>this.radius&&(this.brush.moveByAngle(this.angle,this.distance-this.radius),this._hasMoved=!0)):(this.distance=0,this.angle=0,this.brush.update(e),this._hasMoved=!0),!0))}}]),e}();t.default=u},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Point=t.Catenary=void 0;var i=o(n(9)),r=o(n(2));function o(e){return e&&e.__esModule?e:{default:e}}t.Catenary=i.default,t.Point=r.default},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,r=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}(),o=n(2),a=(i=o)&&i.__esModule?i:{default:i};var s=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.segments,i=void 0===n?50:n,r=t.iterationLimit,o=void 0===r?100:r;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.p1=new a.default,this.p2=new a.default,this.segments=i,this.iterationLimit=o}return r(e,[{key:"drawToCanvas",value:function(e,t,n,i){this.p1.update(t),this.p2.update(n);var r=this.p1.x>this.p2.x,o=r?this.p2:this.p1,a=r?this.p1:this.p2,s=[],u=!0;if(o.getDistanceTo(a)<i)if(a.x-o.x>.01){var c=a.x-o.x,h=a.y-o.y,d=-this.getCatenaryParameter(c,h,i,this.iterationLimit),f=.5*(d*Math.log((i+h)/(i-h))-c),l=d*Math.cosh(f/d),p=o.x-f,v=o.y-l;s=this.getCurve(d,o,a,p,v,this.segments),u=!1}else{var y=.5*(o.x+a.x),b=.5*(o.y+a.y+i);s=[[o.x,o.y],[y,b],[a.x,a.y]]}else s=[[o.x,o.y],[a.x,a.y]];return u?this.drawLine(s,e):this.drawCurve(s,e),s}},{key:"getCatenaryParameter",value:function(e,t,n,i){for(var r=Math.sqrt(n*n-t*t)/e,o=Math.acosh(r)+1,a=-1,s=0;Math.abs(o-a)>1e-6&&s<i;)a=o,o-=(Math.sinh(o)-r*o)/(Math.cosh(o)-r),s++;return e/(2*o)}},{key:"getCurve",value:function(e,t,n,i,r,o){for(var a=[t.x,e*Math.cosh((t.x-i)/e)+r],s=n.x-t.x,u=o-1,c=0;c<u;c++){var h=t.x+s*(c+.5)/u,d=e*Math.cosh((h-i)/e)+r;a.push(h,d)}return a.push(n.x,e*Math.cosh((n.x-i)/e)+r),a}},{key:"drawLine",value:function(e,t){t.moveTo(e[0][0],e[0][1]),t.lineTo(e[1][0],e[1][1])}},{key:"drawCurve",value:function(e,t){var n=.5*e.length-1,i=e[2],r=e[3],o=[];t.moveTo(e[0],e[1]);for(var a=2;a<n;a++){var s=e[2*a],u=e[2*a+1],c=.5*(s+i),h=.5*(u+r);o.push([i,r,c,h]),t.quadraticCurveTo(i,r,c,h),i=s,r=u}return n=e.length,t.quadraticCurveTo(e[n-4],e[n-3],e[n-2],e[n-1]),o}}]),e}();t.default=s},function(e,t,n){"use strict";(function(e){var n=function(){if("undefined"!=typeof Map)return Map;function e(e,t){var n=-1;return e.some(function(e,i){return e[0]===t&&(n=i,!0)}),n}return function(){function t(){this.__entries__=[]}var n={size:{configurable:!0}};return n.size.get=function(){return this.__entries__.length},t.prototype.get=function(t){var n=e(this.__entries__,t),i=this.__entries__[n];return i&&i[1]},t.prototype.set=function(t,n){var i=e(this.__entries__,t);~i?this.__entries__[i][1]=n:this.__entries__.push([t,n])},t.prototype.delete=function(t){var n=this.__entries__,i=e(n,t);~i&&n.splice(i,1)},t.prototype.has=function(t){return!!~e(this.__entries__,t)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(e,t){void 0===t&&(t=null);for(var n=0,i=this.__entries__;n<i.length;n+=1){var r=i[n];e.call(t,r[1],r[0])}},Object.defineProperties(t.prototype,n),t}()}(),i="undefined"!=typeof window&&"undefined"!=typeof document&&window.document===document,r=void 0!==e&&e.Math===Math?e:"undefined"!=typeof self&&self.Math===Math?self:"undefined"!=typeof window&&window.Math===Math?window:Function("return this")(),o="function"==typeof requestAnimationFrame?requestAnimationFrame.bind(r):function(e){return setTimeout(function(){return e(Date.now())},1e3/60)},a=2,s=["top","right","bottom","left","width","height","size","weight"],u="undefined"!=typeof MutationObserver,c=function(){this.connected_=!1,this.mutationEventsAdded_=!1,this.mutationsObserver_=null,this.observers_=[],this.onTransitionEnd_=this.onTransitionEnd_.bind(this),this.refresh=function(e,t){var n=!1,i=!1,r=0;function s(){n&&(n=!1,e()),i&&c()}function u(){o(s)}function c(){var e=Date.now();if(n){if(e-r<a)return;i=!0}else n=!0,i=!1,setTimeout(u,t);r=e}return c}(this.refresh.bind(this),20)};c.prototype.addObserver=function(e){~this.observers_.indexOf(e)||this.observers_.push(e),this.connected_||this.connect_()},c.prototype.removeObserver=function(e){var t=this.observers_,n=t.indexOf(e);~n&&t.splice(n,1),!t.length&&this.connected_&&this.disconnect_()},c.prototype.refresh=function(){this.updateObservers_()&&this.refresh()},c.prototype.updateObservers_=function(){var e=this.observers_.filter(function(e){return e.gatherActive(),e.hasActive()});return e.forEach(function(e){return e.broadcastActive()}),e.length>0},c.prototype.connect_=function(){i&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),u?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},c.prototype.disconnect_=function(){i&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},c.prototype.onTransitionEnd_=function(e){var t=e.propertyName;void 0===t&&(t=""),s.some(function(e){return!!~t.indexOf(e)})&&this.refresh()},c.getInstance=function(){return this.instance_||(this.instance_=new c),this.instance_},c.instance_=null;var h=function(e,t){for(var n=0,i=Object.keys(t);n<i.length;n+=1){var r=i[n];Object.defineProperty(e,r,{value:t[r],enumerable:!1,writable:!1,configurable:!0})}return e},d=function(e){return e&&e.ownerDocument&&e.ownerDocument.defaultView||r},f=g(0,0,0,0);function l(e){return parseFloat(e)||0}function p(e){for(var t=[],n=arguments.length-1;n-- >0;)t[n]=arguments[n+1];return t.reduce(function(t,n){return t+l(e["border-"+n+"-width"])},0)}function v(e){var t=e.clientWidth,n=e.clientHeight;if(!t&&!n)return f;var i=d(e).getComputedStyle(e),r=function(e){for(var t={},n=0,i=["top","right","bottom","left"];n<i.length;n+=1){var r=i[n],o=e["padding-"+r];t[r]=l(o)}return t}(i),o=r.left+r.right,a=r.top+r.bottom,s=l(i.width),u=l(i.height);if("border-box"===i.boxSizing&&(Math.round(s+o)!==t&&(s-=p(i,"left","right")+o),Math.round(u+a)!==n&&(u-=p(i,"top","bottom")+a)),!function(e){return e===d(e).document.documentElement}(e)){var c=Math.round(s+o)-t,h=Math.round(u+a)-n;1!==Math.abs(c)&&(s-=c),1!==Math.abs(h)&&(u-=h)}return g(r.left,r.top,s,u)}var y="undefined"!=typeof SVGGraphicsElement?function(e){return e instanceof d(e).SVGGraphicsElement}:function(e){return e instanceof d(e).SVGElement&&"function"==typeof e.getBBox};function b(e){return i?y(e)?function(e){var t=e.getBBox();return g(0,0,t.width,t.height)}(e):v(e):f}function g(e,t,n,i){return{x:e,y:t,width:n,height:i}}var m=function(e){this.broadcastWidth=0,this.broadcastHeight=0,this.contentRect_=g(0,0,0,0),this.target=e};m.prototype.isActive=function(){var e=b(this.target);return this.contentRect_=e,e.width!==this.broadcastWidth||e.height!==this.broadcastHeight},m.prototype.broadcastRect=function(){var e=this.contentRect_;return this.broadcastWidth=e.width,this.broadcastHeight=e.height,e};var w=function(e,t){var n,i,r,o,a,s,u,c=(i=(n=t).x,r=n.y,o=n.width,a=n.height,s="undefined"!=typeof DOMRectReadOnly?DOMRectReadOnly:Object,u=Object.create(s.prototype),h(u,{x:i,y:r,width:o,height:a,top:r,right:i+o,bottom:a+r,left:i}),u);h(this,{target:e,contentRect:c})},_=function(e,t,i){if(this.activeObservations_=[],this.observations_=new n,"function"!=typeof e)throw new TypeError("The callback provided as parameter 1 is not a function.");this.callback_=e,this.controller_=t,this.callbackCtx_=i};_.prototype.observe=function(e){if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");if("undefined"!=typeof Element&&Element instanceof Object){if(!(e instanceof d(e).Element))throw new TypeError('parameter 1 is not of type "Element".');var t=this.observations_;t.has(e)||(t.set(e,new m(e)),this.controller_.addObserver(this),this.controller_.refresh())}},_.prototype.unobserve=function(e){if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");if("undefined"!=typeof Element&&Element instanceof Object){if(!(e instanceof d(e).Element))throw new TypeError('parameter 1 is not of type "Element".');var t=this.observations_;t.has(e)&&(t.delete(e),t.size||this.controller_.removeObserver(this))}},_.prototype.disconnect=function(){this.clearActive(),this.observations_.clear(),this.controller_.removeObserver(this)},_.prototype.gatherActive=function(){var e=this;this.clearActive(),this.observations_.forEach(function(t){t.isActive()&&e.activeObservations_.push(t)})},_.prototype.broadcastActive=function(){if(this.hasActive()){var e=this.callbackCtx_,t=this.activeObservations_.map(function(e){return new w(e.target,e.broadcastRect())});this.callback_.call(e,t,e),this.clearActive()}},_.prototype.clearActive=function(){this.activeObservations_.splice(0)},_.prototype.hasActive=function(){return this.activeObservations_.length>0};var x="undefined"!=typeof WeakMap?new WeakMap:new n,M=function(e){if(!(this instanceof M))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var t=c.getInstance(),n=new _(e,t,this);x.set(this,n)};["observe","unobserve","disconnect"].forEach(function(e){M.prototype[e]=function(){return(t=x.get(this))[e].apply(t,arguments);var t}});var O=void 0!==r.ResizeObserver?r.ResizeObserver:M;t.a=O}).call(t,n(11))},function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";t.a=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.ctx,n=e.img,i=e.x,r=e.y,o=e.w,a=e.h,s=e.offsetX,u=e.offsetY;"number"!=typeof i&&(i=0);"number"!=typeof r&&(r=0);"number"!=typeof o&&(o=t.canvas.width);"number"!=typeof a&&(a=t.canvas.height);"number"!=typeof s&&(s=.5);"number"!=typeof u&&(u=.5);s<0&&(s=0);u<0&&(u=0);s>1&&(s=1);u>1&&(u=1);var c,h,d,f,l=n.width,p=n.height,v=Math.min(o/l,a/p),y=l*v,b=p*v,g=1;y<o&&(g=o/y);Math.abs(g-1)<1e-14&&b<a&&(g=a/b);h=(p-(f=p/((b*=g)/a)))*u,(c=(l-(d=l/((y*=g)/o)))*s)<0&&(c=0);h<0&&(h=0);d>l&&(d=l);f>p&&(f=p);t.drawImage(n,c,h,d,f,i,r,o,a)}}]).default}); | ||
//# sourceMappingURL=react-canvas-draw.min.js.map |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
279081
10
3300
4
92
7
+ Addedcatenary-curve@^1.0.1
+ Addedlazy-brush@^1.0.1
+ Addedcatenary-curve@1.0.1(transitive)
+ Addedlazy-brush@1.0.1(transitive)
+ Addedresize-observer-polyfill@1.5.1(transitive)