UNPKG

react-drawing-board

Version:

Browser drawing board created with canvas and React.

1,223 lines (1,035 loc) 43.5 kB
"use strict"; function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _Tool = _interopRequireWildcard(require("./enums/Tool")); var _reactIntl = require("react-intl"); var _utils = require("./utils"); var _StrokeTool = require("./StrokeTool"); var _ShapeTool = require("./ShapeTool"); var _ImageTool = require("./ImageTool"); var _TextTool = require("./TextTool"); var _SelectTool = require("./SelectTool"); var _lodash = require("lodash"); var _Icon = _interopRequireDefault(require("./icons/Icon")); var _uuid = require("uuid"); var _sketch_stroke_cursor = _interopRequireDefault(require("./images/sketch_stroke_cursor")); var _gesture = require("./gesture"); var _EnableSketchPadContext = _interopRequireDefault(require("./contexts/EnableSketchPadContext")); require("./SketchPad.css"); var _ConfigContext = _interopRequireDefault(require("./ConfigContext")); var _reactUseGesture = require("react-use-gesture"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var DPR = window.devicePixelRatio || 1; var SELECT_BOX_PADDING = 3; var BackgroundOperationId = 'Background/' + (0, _uuid.v4)(); var stopPropagation = function stopPropagation(e) { return e.stopPropagation(); }; var reduceOperations = function reduceOperations(operations) { var undoHistory = []; operations = operations.sort(function (a, b) { return a.timestamp - b.timestamp; }); // convert background image to draw image var backgroundOperation; operations = operations.reduce(function (r, v) { if (v.tool === _Tool.default.Background) { backgroundOperation = v; return r; } else if (v.tool === _Tool.default.RemoveBackground) { backgroundOperation = undefined; return r; } else { r.push(v); return r; } }, []); operations = operations.reduce(function (r, v) { switch (v.tool) { case _Tool.default.Undo: if (r.length) { undoHistory.push(r.pop()); } break; case _Tool.default.Redo: if (undoHistory.length) { r.push(undoHistory.pop()); } break; default: undoHistory.splice(0); r.push(v); break; } return r; }, []); operations.forEach(function (v) { if (v.tool === _Tool.default.Update) { var update = v; var targetIndex = operations.findIndex(function (w) { return w && w.id === update.operationId; }); if (~targetIndex) { var target = operations[targetIndex]; // @ts-ignore operations[targetIndex] = _objectSpread(_objectSpread({}, operations[targetIndex]), update.data); // move other properties related to pos if (update.data.pos) { switch (target.tool) { case _Tool.default.Eraser: case _Tool.default.Stroke: operations[targetIndex] = _objectSpread(_objectSpread({}, operations[targetIndex]), { points: (0, _StrokeTool.moveStoke)(target, target.pos, update.data.pos) }); break; case _Tool.default.Shape: { var newOperation = _objectSpread({}, operations[targetIndex]); newOperation.start = { x: newOperation.pos.x, y: newOperation.pos.y }; newOperation.end = { x: newOperation.pos.x + newOperation.pos.w, y: newOperation.pos.y + newOperation.pos.h }; operations[targetIndex] = _objectSpread({}, newOperation); break; } default: break; } } } } }); var removeIds = operations.filter(function (v) { return v.tool === _Tool.default.Remove; }).map(function (v) { return v.operationId; }); operations = operations.filter(function (v) { return v.tool !== _Tool.default.Update && removeIds.indexOf(v.id) < 0; }); // keep Remove operation to keep undoable if (backgroundOperation) { operations.unshift(backgroundOperation); } return operations; }; var operationListReducer = function operationListReducer(isControlled, onChange) { return function (state, action) { switch (action.type) { case 'add': { var operation = action.payload.operation; var isLazy = action.payload.isLazy; var newQueue = state.queue.concat([operation]); if (!isControlled || isLazy) { return { queue: newQueue, reduced: reduceOperations(newQueue) }; } else { onChange && onChange(operation, newQueue); return state; } } case 'replaceLast': { var _operation = action.payload.operation; var _newQueue = state.queue.slice(0, -1).concat([_operation]); return { queue: _newQueue, reduced: reduceOperations(_newQueue) }; } case 'replaceAll': { var _newQueue2 = action.payload.queue; return { queue: _newQueue2, reduced: reduceOperations(_newQueue2) }; } case 'completeLazyUpdate': { var _operation2 = state.queue[state.queue.length - 1]; if (isControlled && _operation2 && _operation2.tool === _Tool.default.Update) { onChange && onChange(_operation2, state.queue); } return state; } default: return state; } }; }; var ResizeDirection; (function (ResizeDirection) { ResizeDirection["TopLeft"] = "TopLeft"; ResizeDirection["TopCenter"] = "TopCenter"; ResizeDirection["MiddleRight"] = "MiddleRight"; ResizeDirection["MiddleLeft"] = "MiddleLest"; ResizeDirection["BottomRight"] = "BottomRight"; ResizeDirection["BottomCenter"] = "BottomCenter"; ResizeDirection["BottomLeft"] = "BottomLeft"; })(ResizeDirection || (ResizeDirection = {})); var isResizing = null; var startResizePoint = [0, 0]; var startResizePos = null; var useResizeHandler = function useResizeHandler(selectedOperation, viewMatrix, items, operationListDispatch, setSelectedOperation, handleCompleteOperation, refCanvas, prefixCls) { if (selectedOperation && (selectedOperation.tool === _Tool.default.Shape || selectedOperation.tool === _Tool.default.Image)) { var _viewMatrix = _slicedToArray(viewMatrix, 6), a = _viewMatrix[0], b = _viewMatrix[1], c = _viewMatrix[2], d = _viewMatrix[3], e = _viewMatrix[4], f = _viewMatrix[5]; var pos = { x: selectedOperation.pos.x - SELECT_BOX_PADDING, y: selectedOperation.pos.y - SELECT_BOX_PADDING, w: selectedOperation.pos.w + 2 * SELECT_BOX_PADDING, h: selectedOperation.pos.h + 2 * SELECT_BOX_PADDING }; var tl = [a * pos.x + c * pos.y + e, b * pos.x + d * pos.y + f]; var br = [a * (pos.x + pos.w) + c * (pos.y + pos.h) + e, b * (pos.x + pos.w) + d * (pos.y + pos.h) + f]; var w = br[0] - tl[0], h = br[1] - tl[1]; var onMouseDown = function onMouseDown(direction) { return function (e) { e.stopPropagation(); if (refCanvas.current) { isResizing = direction; startResizePoint = (0, _utils.mapClientToCanvas)(e, refCanvas.current, viewMatrix); startResizePos = _objectSpread({}, selectedOperation.pos); } }; }; var onTouchStart = function onTouchStart(direction) { return function (e) { e.stopPropagation(); if (refCanvas.current && e.touches[0]) { isResizing = direction; startResizePoint = (0, _utils.mapClientToCanvas)(e.touches[0], refCanvas.current, viewMatrix); startResizePos = _objectSpread({}, selectedOperation.pos); } }; }; var onMouseMove = function onMouseMove(e) { if (selectedOperation && isResizing && refCanvas.current && startResizePos) { var _pos = (0, _utils.mapClientToCanvas)(e, refCanvas.current, viewMatrix); var diff = { x: _pos[0] - startResizePoint[0], y: _pos[1] - startResizePoint[1] }; var updatePos = _objectSpread({}, startResizePos); if (isResizing === ResizeDirection.TopLeft) { diff.x = Math.min(diff.x, updatePos.w); diff.y = Math.min(diff.y, updatePos.h); updatePos.x += diff.x; updatePos.y += diff.y; updatePos.w -= diff.x; updatePos.h -= diff.y; } else if (isResizing === ResizeDirection.TopCenter) { diff.y = Math.min(diff.y, updatePos.h); updatePos.y += diff.y; updatePos.h -= diff.y; } else if (isResizing === ResizeDirection.MiddleRight) { diff.x = Math.max(diff.x, -updatePos.w); updatePos.w += diff.x; } else if (isResizing === ResizeDirection.BottomRight) { diff.x = Math.max(diff.x, -updatePos.w); diff.y = Math.max(diff.y, -updatePos.h); updatePos.w += diff.x; updatePos.h += diff.y; } else if (isResizing === ResizeDirection.BottomCenter) { diff.y = Math.max(diff.y, -updatePos.h); updatePos.h += diff.y; } else if (isResizing === ResizeDirection.BottomLeft) { diff.y = Math.max(diff.y, -updatePos.h); diff.x = Math.min(diff.x, updatePos.w); updatePos.x += diff.x; updatePos.w -= diff.x; updatePos.h += diff.y; } else if (isResizing === ResizeDirection.MiddleLeft) { diff.x = Math.min(diff.x, updatePos.w); updatePos.x += diff.x; updatePos.w -= diff.x; } var lastOperation = items[items.length - 1]; if (lastOperation && lastOperation.tool === _Tool.default.Update && lastOperation.operationId === selectedOperation.id && lastOperation.data.pos) { var update = lastOperation; if (update.data.pos) { update.data.pos = _objectSpread({}, updatePos); operationListDispatch({ type: 'replaceLast', payload: { operation: update } }); } } else { handleCompleteOperation(_Tool.default.LazyUpdate, { operationId: selectedOperation.id, data: { pos: _objectSpread({}, updatePos) } }); } setSelectedOperation(_objectSpread(_objectSpread({}, selectedOperation), {}, { pos: _objectSpread({}, updatePos) })); } }; var onMouseUp = function onMouseUp() { operationListDispatch({ type: 'completeLazyUpdate' }); isResizing = null; }; return { onMouseMove: onMouseMove, onMouseUp: onMouseUp, resizer: /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", { key: ResizeDirection.TopLeft, onTouchStart: onTouchStart(ResizeDirection.TopLeft), onMouseDown: onMouseDown(ResizeDirection.TopLeft), className: "".concat(prefixCls, "-resizer"), style: { left: tl[0] + 'px', top: tl[1] + 'px' } }), /*#__PURE__*/_react.default.createElement("div", { key: ResizeDirection.TopCenter, onTouchStart: onTouchStart(ResizeDirection.TopCenter), onMouseDown: onMouseDown(ResizeDirection.TopCenter), className: "".concat(prefixCls, "-resizer"), style: { left: tl[0] + w / 2 + 'px', top: tl[1] + 'px' } }), /*#__PURE__*/_react.default.createElement("div", { key: ResizeDirection.MiddleRight, onTouchStart: onTouchStart(ResizeDirection.MiddleRight), onMouseDown: onMouseDown(ResizeDirection.MiddleRight), className: "".concat(prefixCls, "-resizer"), style: { left: tl[0] + w + 'px', top: tl[1] + h / 2 + 'px' } }), /*#__PURE__*/_react.default.createElement("div", { key: ResizeDirection.BottomRight, onTouchStart: onTouchStart(ResizeDirection.BottomRight), onMouseDown: onMouseDown(ResizeDirection.BottomRight), className: "".concat(prefixCls, "-resizer"), style: { left: br[0] + 'px', top: br[1] + 'px' } }), /*#__PURE__*/_react.default.createElement("div", { key: ResizeDirection.BottomCenter, onTouchStart: onTouchStart(ResizeDirection.BottomCenter), onMouseDown: onMouseDown(ResizeDirection.BottomCenter), className: "".concat(prefixCls, "-resizer"), style: { left: br[0] - w / 2 + 'px', top: br[1] + 'px' } }), /*#__PURE__*/_react.default.createElement("div", { key: ResizeDirection.BottomLeft, onTouchStart: onTouchStart(ResizeDirection.BottomLeft), onMouseDown: onMouseDown(ResizeDirection.BottomLeft), className: "".concat(prefixCls, "-resizer"), style: { left: br[0] - w + 'px', top: br[1] + 'px' } }), /*#__PURE__*/_react.default.createElement("div", { key: ResizeDirection.MiddleLeft, onTouchStart: onTouchStart(ResizeDirection.MiddleLeft), onMouseDown: onMouseDown(ResizeDirection.MiddleLeft), className: "".concat(prefixCls, "-resizer"), style: { left: tl[0] + 'px', top: tl[1] + h / 2 + 'px' } })) }; } else return { onMouseMove: function onMouseMove() {}, onMouseUp: function onMouseUp() {}, resizer: null }; }; var SketchPad = function SketchPad(props, ref) { var currentTool = props.currentTool, setCurrentTool = props.setCurrentTool, userId = props.userId, currentToolOption = props.currentToolOption, operations = props.operations, initialBackground = props.initialBackground, onChange = props.onChange, viewMatrix = props.viewMatrix, onViewMatrixChange = props.onViewMatrixChange; var refCanvas = (0, _react.useRef)(null); var refContext = (0, _react.useRef)(null); var refInput = (0, _react.useRef)(null); var lastTapRef = (0, _react.useRef)(0); var intl = (0, _reactIntl.useIntl)(); var _useContext = (0, _react.useContext)(_ConfigContext.default), prefixCls = _useContext.prefixCls; var enableSketchPadContext = (0, _react.useContext)(_EnableSketchPadContext.default); var sketchpadPrefixCls = prefixCls + '-sketchpad'; var _useState = (0, _react.useState)(null), _useState2 = _slicedToArray(_useState, 2), hoverOperationId = _useState2[0], setHoverOperationId = _useState2[1]; var _useState3 = (0, _react.useState)(null), _useState4 = _slicedToArray(_useState3, 2), selectedOperation = _useState4[0], setSelectedOperation = _useState4[1]; var isControlled = !!operations; var scale = (0, _utils.extract_scale_from_matrix)(viewMatrix); var reducer = (0, _react.useCallback)(operationListReducer(isControlled, onChange), []); var _useReducer = (0, _react.useReducer)(reducer, { queue: [], reduced: [] }), _useReducer2 = _slicedToArray(_useReducer, 2), operationListState = _useReducer2[0], operationListDispatch = _useReducer2[1]; if (isControlled) { (0, _react.useEffect)(function () { operationListDispatch({ type: 'replaceAll', payload: { queue: operations } }); }, [operations.length]); } var refOperationListState = (0, _react.useRef)(operationListState); refOperationListState.current = operationListState; var saveGlobalTransform = function saveGlobalTransform() { if (!refContext.current) return; var context = refContext.current; context.save(); context.scale(DPR, DPR); var _viewMatrix2 = _slicedToArray(viewMatrix, 6), a = _viewMatrix2[0], b = _viewMatrix2[1], c = _viewMatrix2[2], d = _viewMatrix2[3], e = _viewMatrix2[4], f = _viewMatrix2[5]; context.transform(a, b, c, d, e, f); }; var restoreGlobalTransform = function restoreGlobalTransform() { if (!refContext.current) return; var context = refContext.current; context.restore(); }; var renderOperations = function renderOperations(operations) { if (!refContext.current || !refCanvas.current) return; var context = refContext.current; // clear canvas context.setTransform(1, 0, 0, 1, 0, 0); context.clearRect(0, 0, context.canvas.width, context.canvas.height); saveGlobalTransform(); var renderBackgroundOperation = function renderBackgroundOperation(operation) { // restoreGlobalTransform(); (0, _ImageTool.drawBackgroundImage)(operation, refCanvas.current, context, viewMatrix, operation.id, function () { renderOperations(operations); }); // saveGlobalTransform(); }; var backgroundOperation; if (initialBackground) { backgroundOperation = { id: "".concat(BackgroundOperationId, "/").concat(initialBackground), userId: userId, timestamp: Date.now(), tool: _Tool.default.Background, imageData: initialBackground }; renderBackgroundOperation(backgroundOperation); } operations.forEach(function (operation) { var hover = (!selectedOperation || selectedOperation.id !== operation.id) && operation.id === hoverOperationId; switch (operation.tool) { case _Tool.default.Clear: restoreGlobalTransform(); context.clearRect(0, 0, context.canvas.width, context.canvas.height); saveGlobalTransform(); // background image should not be removed if (backgroundOperation) { renderBackgroundOperation(backgroundOperation); } break; case _Tool.default.Eraser: case _Tool.default.Stroke: (0, _StrokeTool.drawStroke)(operation, context, hover); break; case _Tool.default.Shape: (0, _ShapeTool.drawRectangle)(operation, context, hover); break; case _Tool.default.Text: (0, _TextTool.drawText)(operation, context, operation.pos); break; case _Tool.default.Background: backgroundOperation = operation; renderBackgroundOperation(backgroundOperation); break; case _Tool.default.Image: (0, _ImageTool.drawImage)(operation, context, operation.pos, operation.id, function () { renderOperations(operations); }); default: break; } }); // selected box if (selectedOperation) { context.beginPath(); context.lineWidth = 1; context.strokeStyle = '#d0d0d0'; context.rect(selectedOperation.pos.x - SELECT_BOX_PADDING, selectedOperation.pos.y - SELECT_BOX_PADDING, selectedOperation.pos.w + 2 * SELECT_BOX_PADDING, selectedOperation.pos.h + 2 * SELECT_BOX_PADDING); context.stroke(); context.closePath(); } restoreGlobalTransform(); }; (0, _react.useEffect)(function () { var keydownHandler = function keydownHandler(evt) { var keyCode = evt.keyCode; // key 'delete' if (keyCode === 8) { if (selectedOperation) { setSelectedOperation(null); handleCompleteOperation(_Tool.default.Remove, { operationId: selectedOperation.id }); } } else if (keyCode === 27) { // key 'esc' setSelectedOperation(null); } }; addEventListener('keydown', keydownHandler); return function () { return removeEventListener('keydown', keydownHandler); }; }, [selectedOperation && selectedOperation.id]); (0, _react.useEffect)(function () { var resizeHandler = (0, _lodash.debounce)(function () { var canvas = refCanvas.current; if (canvas && refOperationListState.current) { // high resolution canvas. var rect = canvas.getBoundingClientRect(); canvas.width = rect.width * DPR; canvas.height = rect.height * DPR; renderOperations(refOperationListState.current.reduced); } }, 200); addEventListener('resize', resizeHandler); return function () { return removeEventListener('resize', resizeHandler); }; }, []); // disable default scrolling on mobile device. // refer: https://stackoverflow.com/questions/49500339/cant-prevent-touchmove-from-scrolling-window-on-ios (0, _react.useEffect)(function () { var handler = function handler(e) { // only disable scroll when interact with this board. if (lastTapRef.current) { e.preventDefault(); } onTouchMoveRef.current && onTouchMoveRef.current(e); }; document.addEventListener('touchmove', handler, { passive: false }); return function () { document.removeEventListener('touchmove', handler); }; }, []); var handleCompleteOperation = function handleCompleteOperation(tool, data, pos) { if (!tool) { renderOperations(operationListState.reduced); return; } // coerce update. var isLazy = tool === _Tool.default.LazyUpdate; tool = isLazy ? _Tool.default.Update : tool; var message = Object.assign({}, data, { id: (0, _uuid.v4)(), userId: userId, timestamp: Date.now(), pos: pos, tool: tool }); operationListDispatch({ type: 'add', payload: { operation: message, isLazy: isLazy } }); }; var _useResizeHandler = useResizeHandler(selectedOperation, viewMatrix, operationListState.queue, operationListDispatch, setSelectedOperation, handleCompleteOperation, refCanvas, sketchpadPrefixCls), onMouseResizeMove = _useResizeHandler.onMouseMove, onMouseResizeUp = _useResizeHandler.onMouseUp, resizer = _useResizeHandler.resizer; var onMouseDown = function onMouseDown(e) { if (!refCanvas.current) return null; if (!enableSketchPadContext.enable) return null; var _mapClientToCanvas = (0, _utils.mapClientToCanvas)(e, refCanvas.current, viewMatrix), _mapClientToCanvas2 = _slicedToArray(_mapClientToCanvas, 2), x = _mapClientToCanvas2[0], y = _mapClientToCanvas2[1]; switch (currentTool) { case _Tool.default.Select: (0, _SelectTool.onSelectMouseDown)(e, x, y, scale, operationListState, viewMatrix, setSelectedOperation); break; case _Tool.default.Stroke: (0, _StrokeTool.onStrokeMouseDown)(x, y, currentToolOption); break; case _Tool.default.Eraser: (0, _StrokeTool.onStrokeMouseDown)(x, y, _objectSpread(_objectSpread({}, currentToolOption), {}, { strokeSize: _Tool.defaultToolOption.strokeSize * 2 / scale, strokeColor: 'rgba(255, 255, 255, 1)' })); break; case _Tool.default.Shape: (0, _ShapeTool.onShapeMouseDown)(x, y, currentToolOption); break; case _Tool.default.Text: (0, _TextTool.onTextMouseDown)(e, currentToolOption, scale, refInput, refCanvas, intl); break; default: break; } }; var onTouchStart = function onTouchStart(e) { if (e.touches.length === 1) { if (e.timeStamp - lastTapRef.current < 300) { onDoubleClick(e.touches[0]); } else { onMouseDown(e.touches[0]); } } lastTapRef.current = e.timeStamp; }; var onDoubleClick = function onDoubleClick(e) { if (!refCanvas.current) return null; var _mapClientToCanvas3 = (0, _utils.mapClientToCanvas)(e, refCanvas.current, viewMatrix), _mapClientToCanvas4 = _slicedToArray(_mapClientToCanvas3, 2), x = _mapClientToCanvas4[0], y = _mapClientToCanvas4[1]; switch (currentTool) { case _Tool.default.Select: (0, _SelectTool.onSelectMouseDoubleClick)(x, y, scale, operationListState, handleCompleteOperation, viewMatrix, refInput, refCanvas, intl); setSelectedOperation(null); break; default: setCurrentTool(_Tool.default.Select); break; } }; var onMouseMove = function onMouseMove(e) { if (!refCanvas.current) return null; if (!enableSketchPadContext.enable) return null; onMouseResizeMove(e); var _mapClientToCanvas5 = (0, _utils.mapClientToCanvas)(e, refCanvas.current, viewMatrix), _mapClientToCanvas6 = _slicedToArray(_mapClientToCanvas5, 2), x = _mapClientToCanvas6[0], y = _mapClientToCanvas6[1]; switch (currentTool) { case _Tool.default.Select: (0, _SelectTool.onSelectMouseMove)(e, x, y, scale, operationListState, selectedOperation, onViewMatrixChange, setHoverOperationId, handleCompleteOperation, operationListDispatch, setSelectedOperation); break; case _Tool.default.Eraser: case _Tool.default.Stroke: { saveGlobalTransform(); refContext.current && (0, _StrokeTool.onStrokeMouseMove)(x, y, refContext.current); restoreGlobalTransform(); break; } case _Tool.default.Shape: { renderOperations(operationListState.reduced); saveGlobalTransform(); refContext.current && (0, _ShapeTool.onShapeMouseMove)(x, y, refContext.current); restoreGlobalTransform(); break; } default: break; } }; var onTouchMove = function onTouchMove(e) { if (e.touches.length === 1) { onMouseMove(e.touches[0]); } }; var onTouchMoveRef = (0, _react.useRef)(onTouchMove); (0, _react.useEffect)(function () { onTouchMoveRef.current = onTouchMove; }, [onTouchMove]); var onMouseUp = function onMouseUp(e) { if (!refCanvas.current) return null; if (!enableSketchPadContext.enable) return null; onMouseResizeUp(e); switch (currentTool) { case _Tool.default.Select: (0, _SelectTool.onSelectMouseUp)(operationListDispatch); break; case _Tool.default.Eraser: { refContext.current && (0, _StrokeTool.onStrokeMouseUp)(setCurrentTool, handleCompleteOperation, _Tool.default.Eraser); break; } case _Tool.default.Stroke: { refContext.current && (0, _StrokeTool.onStrokeMouseUp)(setCurrentTool, handleCompleteOperation); break; } case _Tool.default.Shape: { var _mapClientToCanvas7 = (0, _utils.mapClientToCanvas)(e, refCanvas.current, viewMatrix), _mapClientToCanvas8 = _slicedToArray(_mapClientToCanvas7, 2), x = _mapClientToCanvas8[0], y = _mapClientToCanvas8[1]; refContext.current && (0, _ShapeTool.onShapeMouseUp)(x, y, setCurrentTool, handleCompleteOperation); break; } default: break; } }; var onTouchEnd = function onTouchEnd(e) { if (e.changedTouches.length === 1) { onMouseUp(e.changedTouches[0]); } lastTapRef.current = 0; }; var onWheel = function onWheel(evt) { if (_utils.isMobileDevice && !evt.forceWheel) return; evt.stopPropagation && evt.stopPropagation(); var deltaY = evt.deltaY, ctrlKey = evt.ctrlKey; var _viewMatrix3 = _slicedToArray(viewMatrix, 6), a = _viewMatrix3[0], b = _viewMatrix3[1], c = _viewMatrix3[2], d = _viewMatrix3[3], e = _viewMatrix3[4], f = _viewMatrix3[5]; var newScale = a + (ctrlKey ? -deltaY : deltaY) / 100; newScale = Math.max(Math.min(newScale, _Tool.MAX_SCALE), _Tool.MIN_SCALE); if (refCanvas.current) { var pos = (0, _utils.mapClientToCanvas)(evt, refCanvas.current, viewMatrix); var scaleChange = newScale - a; onViewMatrixChange([newScale, b, c, newScale, e - pos[0] * scaleChange, f - pos[1] * scaleChange]); } setSelectedOperation(null); }; var onRemoveOperation = function onRemoveOperation(evt) { evt.preventDefault(); evt.stopPropagation(); if (selectedOperation) { setSelectedOperation(null); handleCompleteOperation(_Tool.default.Remove, { operationId: selectedOperation.id }); } }; (0, _react.useEffect)(function () { var canvas = refCanvas.current; // high resolution canvas. var rect = canvas.getBoundingClientRect(); canvas.width = rect.width * DPR; canvas.height = rect.height * DPR; refContext.current = canvas.getContext('2d'); canvas.oncontextmenu = function (e) { e.preventDefault(); }; }, []); (0, _react.useEffect)(function () { renderOperations(operationListState.reduced); }, [operationListState.reduced, viewMatrix, hoverOperationId, selectedOperation, initialBackground, refContext.current]); var canvasStyle = {}; if (currentTool === _Tool.default.Stroke) { canvasStyle.cursor = "url(".concat(_sketch_stroke_cursor.default, ") 0 14, crosshair"); } else if (currentTool === _Tool.default.Shape) { canvasStyle.cursor = "crosshair"; } else if (currentTool === _Tool.default.Text) { canvasStyle.cursor = "text"; } (0, _react.useImperativeHandle)(ref, function () { return { selectImage: function selectImage(image) { if (image && refCanvas.current) { (0, _ImageTool.onImageComplete)(image, refCanvas.current, viewMatrix, handleCompleteOperation); } }, selectBackgroundImage: function selectBackgroundImage(image) { if (image && refCanvas.current) { (0, _ImageTool.onBackgroundImageComplete)(image, refCanvas.current, viewMatrix, handleCompleteOperation); } }, removeBackgroundImage: function removeBackgroundImage() { if (refCanvas.current) { handleCompleteOperation(_Tool.default.RemoveBackground); } }, undo: function undo() { setSelectedOperation(null); if (operationListState.reduced.length) { handleCompleteOperation(_Tool.default.Undo); } }, redo: function redo() { setSelectedOperation(null); var isRedoable = 0; var queue = operationListState.queue; for (var i = queue.length - 1; i >= 0; i--) { if (queue[i].tool === _Tool.default.Undo) { isRedoable++; } else if (queue[i].tool === _Tool.default.Redo) { isRedoable--; } else { break; } } if (isRedoable > 0) { handleCompleteOperation(_Tool.default.Redo); } }, clear: function clear() { setSelectedOperation(null); handleCompleteOperation(_Tool.default.Clear); }, save: function save(handleSave) { if (refCanvas.current && refContext.current) { var canvas = refCanvas.current; var w = canvas.width; var h = canvas.height; var context = refContext.current; context.globalCompositeOperation = 'destination-over'; context.fillStyle = '#fff'; context.fillRect(0, 0, w, h); var dataUrl = canvas.toDataURL('image/png'); if (handleSave) { handleSave({ canvas: canvas, dataUrl: dataUrl }); } else { var a = document.createElement('a'); a.href = dataUrl; a.download = 'sketch.png'; a.click(); } } } }; }); (0, _gesture.useZoomGesture)(refCanvas); var bindPinch = (0, _reactUseGesture.usePinch)(function (state) { var ctrlKey = state.ctrlKey, origin = state.origin, delta = state.delta; if (origin) { onWheel({ deltaY: delta[0], ctrlKey: ctrlKey, clientX: origin[0], clientY: origin[1], forceWheel: true }); } }); var bindWheel = (0, _reactUseGesture.useWheel)(function (state) { var ctrlKey = state.ctrlKey, event = state.event, delta = state.delta, last = state.last; if (event && !last && 'clientX' in event) { onWheel({ deltaY: delta[1] / 4, ctrlKey: ctrlKey, clientX: event.clientX + 0, clientY: event.clientY + 0, forceWheel: true }); } }); var settingMenu = null; var removeButton = null; if (selectedOperation) { var content = null; switch (selectedOperation.tool) { case _Tool.default.Stroke: content = (0, _StrokeTool.useStrokeDropdown)({ currentToolOption: { strokeSize: selectedOperation.size, strokeColor: selectedOperation.color }, setCurrentToolOption: function setCurrentToolOption(option) { var data = { color: option.strokeColor, size: option.strokeSize }; handleCompleteOperation(_Tool.default.Update, { operationId: selectedOperation.id, data: data }); setSelectedOperation(_objectSpread(_objectSpread({}, selectedOperation), data)); }, setCurrentTool: function setCurrentTool() {}, prefixCls: prefixCls }); break; case _Tool.default.Shape: content = (0, _ShapeTool.useShapeDropdown)({ currentToolOption: { shapeType: selectedOperation.type, shapeBorderColor: selectedOperation.color, shapeBorderSize: selectedOperation.size }, setCurrentToolOption: function setCurrentToolOption(option) { var data = { type: option.shapeType, color: option.shapeBorderColor, size: option.shapeBorderSize }; handleCompleteOperation(_Tool.default.Update, { operationId: selectedOperation.id, data: data }); setSelectedOperation(_objectSpread(_objectSpread({}, selectedOperation), data)); }, setCurrentTool: function setCurrentTool() {}, prefixCls: prefixCls }); break; case _Tool.default.Text: { var textOperation = selectedOperation; content = (0, _TextTool.useTextDropdown)({ textSize: textOperation.size, textColor: textOperation.color }, function (option) { var data = { color: option.textColor, size: option.textSize }; if (refContext.current && option.textSize !== textOperation.size) { var context = refContext.current; // font size has changed, need to update pos context.font = "".concat(option.textSize, "px ").concat(_TextTool.font); context.textBaseline = 'alphabetic'; // measureText does not support multi-line var lines = textOperation.text.split('\n'); data.pos = _objectSpread(_objectSpread({}, selectedOperation.pos), {}, { w: Math.max.apply(Math, _toConsumableArray(lines.map(function (line) { return context.measureText(line).width; }))), h: lines.length * option.textSize }); } handleCompleteOperation(_Tool.default.Update, { operationId: selectedOperation.id, data: data }); // @ts-ignore setSelectedOperation(_objectSpread(_objectSpread({}, selectedOperation), data)); }, function () {}, intl, prefixCls); break; } default: break; } var resultRect = { xMin: selectedOperation.pos.x, xMax: selectedOperation.pos.x + selectedOperation.pos.w, yMin: selectedOperation.pos.y, yMax: selectedOperation.pos.y + selectedOperation.pos.h }; var _viewMatrix4 = _slicedToArray(viewMatrix, 6), a = _viewMatrix4[0], b = _viewMatrix4[1], c = _viewMatrix4[2], d = _viewMatrix4[3], e = _viewMatrix4[4], f = _viewMatrix4[5]; var selectPadding = Math.max(_SelectTool.SELECT_PADDING * 1 / scale || 0, _SelectTool.SELECT_PADDING); var left = resultRect.xMin; var top = resultRect.yMax + selectPadding; var menuStyle = { position: 'absolute', left: a * left + c * top + e, top: b * left + d * top + f }; settingMenu = /*#__PURE__*/_react.default.createElement("div", { style: menuStyle, onMouseDown: stopPropagation }, content); var removeX = selectedOperation.tool === _Tool.default.Text ? resultRect.xMax - 5 / scale : resultRect.xMax - 7 / scale; var removeY = selectedOperation.tool === _Tool.default.Text ? resultRect.yMin - 11 / scale : resultRect.yMin - 9 / scale; var removeStyle = { position: 'absolute', left: a * removeX + c * removeY + e, top: b * removeX + d * removeY + f, background: 'white', lineHeight: '16px', fontSize: '16px', borderRadius: '50%', cursor: 'pointer', color: '#f45b6c' }; removeButton = /*#__PURE__*/_react.default.createElement("div", { style: removeStyle, onMouseDown: onRemoveOperation, onTouchStart: onRemoveOperation }, /*#__PURE__*/_react.default.createElement(_Icon.default, { type: "close-circle" })); } return /*#__PURE__*/_react.default.createElement("div", { className: "".concat(sketchpadPrefixCls, "-container"), onMouseDown: onMouseDown, onMouseMove: onMouseMove, onMouseLeave: onMouseUp, onTouchStart: onTouchStart, onTouchEnd: onTouchEnd, onMouseUp: onMouseUp }, /*#__PURE__*/_react.default.createElement("div", { id: "test" }), /*#__PURE__*/_react.default.createElement("canvas", _objectSpread(_objectSpread({ ref: refCanvas, onDoubleClick: onDoubleClick, className: "".concat(sketchpadPrefixCls, "-canvas"), style: canvasStyle }, bindPinch()), bindWheel())), /*#__PURE__*/_react.default.createElement("div", { ref: refInput, contentEditable: true, style: { fontSize: "".concat(12 * scale, "px") }, className: "".concat(sketchpadPrefixCls, "-textInput"), onBlur: function onBlur() { (0, _TextTool.onTextComplete)(refInput, refCanvas, viewMatrix, scale, handleCompleteOperation, setCurrentTool); } }), settingMenu, removeButton, resizer); }; var _default = /*#__PURE__*/(0, _react.forwardRef)(SketchPad); exports.default = _default;