UNPKG

screenshot-editor-test

Version:

canvas editor

753 lines (675 loc) 25 kB
import React from 'react'; import PropTypes from 'prop-types'; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 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 inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }; var possibleConstructorReturn = function (self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }; var toConsumableArray = function (arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }; require('./styles.css'); var Editor = function (_React$Component) { inherits(Editor, _React$Component); function Editor(props) { classCallCheck(this, Editor); var _this = possibleConstructorReturn(this, (Editor.__proto__ || Object.getPrototypeOf(Editor)).call(this, props)); _this.fillTextOnCanvas = function (ctx) { var _this$state = _this.state, textValue = _this$state.textValue, startX = _this$state.startX, startY = _this$state.startY; var lines = textValue.split('\n'); var offset = 0; ctx.textBaseline = 'top'; ctx.textAlign = 'left'; ctx.font = '14px Arial'; ctx.fillStyle = _this.state.drawingColor; var textEditor = document.querySelector('.screenshotEditor__text'); if (textEditor) { var charWidth = 7, textEditorWidth = textEditor.offsetWidth - 10, charLimit = textEditorWidth / charWidth; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = lines[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var line = _step.value; var words = line.split(" "); var subLines = [words[0]]; var k = 0; for (var i = 1; i < words.length; i++) { var word = words[i]; if (subLines[k].length + word.length + 1 <= charLimit) { subLines[k] = subLines[k] + ' ' + word; } else { k++; subLines[k] = word; } } for (var _i = 0; _i < subLines.length; _i++) { var subLine = subLines[_i]; ctx.fillText(subLine, startX, startY + offset); offset += 20; } offset += 10; } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } }; _this.drawRectangle = function (e) { var _this$state2 = _this.state, startX = _this$state2.startX, startY = _this$state2.startY, canvasWidth = _this$state2.canvasWidth, canvasHeight = _this$state2.canvasHeight; var ctx = _this.editorCanvasRef.current.getContext('2d'); var rect = e.target.getBoundingClientRect(); var x = e.clientX - rect.left; var y = e.clientY - rect.top; var width = x - startX; var height = y - startY; ctx.clearRect(0, 0, canvasWidth, canvasHeight); ctx.beginPath(); ctx.rect(startX, startY, width, height); ctx.stroke(); }; _this.drawCircle = function (e) { var _this$state3 = _this.state, startX = _this$state3.startX, startY = _this$state3.startY, canvasWidth = _this$state3.canvasWidth, canvasHeight = _this$state3.canvasHeight; var ctx = _this.editorCanvasRef.current.getContext('2d'); var rect = e.target.getBoundingClientRect(); var x = e.clientX - rect.left; var y = e.clientY - rect.top; var circleX = (x + startX) / 2; var circleY = (y + startY) / 2; var radius = Math.max(Math.abs(x - startX), Math.abs(y - startY)) / 2; ctx.clearRect(0, 0, canvasWidth, canvasHeight); ctx.beginPath(); ctx.arc(circleX, circleY, radius, 0, Math.PI * 2); ctx.stroke(); ctx.closePath(); }; _this.drawLine = function (e) { var _this$state4 = _this.state, startX = _this$state4.startX, startY = _this$state4.startY, canvasWidth = _this$state4.canvasWidth, canvasHeight = _this$state4.canvasHeight; var ctx = _this.editorCanvasRef.current.getContext('2d'); var rect = e.target.getBoundingClientRect(); var x = e.clientX - rect.left; var y = e.clientY - rect.top; ctx.clearRect(0, 0, canvasWidth, canvasHeight); ctx.beginPath(); ctx.moveTo(startX, startY); ctx.lineTo(x, y); ctx.stroke(); ctx.closePath(); }; _this.drawPencil = function (e) { var ctx = _this.editorCanvasRef.current.getContext('2d'); var rect = e.target.getBoundingClientRect(); var x = e.clientX - rect.left; var y = e.clientY - rect.top; ctx.lineTo(x, y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(x, y); ctx.closePath(); }; _this.onErasae = function (e) { var ctx = _this.baseCanvas.current.getContext('2d'); var rect = e.target.getBoundingClientRect(); var x = e.clientX - rect.left; var y = e.clientY - rect.top; ctx.lineTo(x, y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(x, y); }; _this.handleMouseDown = function (e) { var _this$state5 = _this.state, drawingShape = _this$state5.drawingShape, textValue = _this$state5.textValue, startX = _this$state5.startX, startY = _this$state5.startY, canvasWidth = _this$state5.canvasWidth, canvasHeight = _this$state5.canvasHeight, lineWidth = _this$state5.lineWidth, drawingColor = _this$state5.drawingColor; var baseImageCavnas = _this.imageAndDrawingMixingCanvas.current.getContext('2d'); var ctx = _this.editorCanvasRef.current.getContext('2d'); var downCtx = _this.baseCanvas.current.getContext('2d'); baseImageCavnas.clearRect(0, 0, canvasWidth, canvasHeight); ctx.clearRect(0, 0, canvasWidth, canvasHeight); ctx.strokeStyle = drawingColor; ctx.fillStyle = drawingColor; if (drawingShape === 'text') { _this.fillTextOnCanvas(ctx); _this.setState({ textValue: '', hideTextBox: false }); } else { _this.setState({ hideTextBox: true }); } if (drawingShape === 'eraser') { ctx.globalCompositeOperation = 'destination-out'; downCtx.globalCompositeOperation = 'destination-out'; downCtx.fillStyle = 'rgba(0,0,0,1)'; downCtx.strokeStyle = 'rgba(0,0,0,1)'; downCtx.lineWidth = lineWidth * 2; } else { ctx.globalCompositeOperation = 'source-over'; downCtx.globalCompositeOperation = 'source-over'; ctx.lineWidth = lineWidth; ctx.lineCap = 'round'; } downCtx.beginPath(); ctx.beginPath(); var rect = e.target.getBoundingClientRect(); var x = e.clientX - rect.left; var y = e.clientY - rect.top; _this.setState({ startX: x, startY: y, isMouseDown: true }); }; _this.handleMouseMove = function (e) { var _this$state6 = _this.state, isMouseDown = _this$state6.isMouseDown, startX = _this$state6.startX, startY = _this$state6.startY, drawingShape = _this$state6.drawingShape; if (isMouseDown && startX && startY) { switch (drawingShape) { case 'rect': _this.drawRectangle(e); break; case 'circle': _this.drawCircle(e); break; case 'line': _this.drawLine(e); break; case 'pencil': _this.drawPencil(e); break; case 'eraser': _this.onErasae(e); break; default: break; } } }; _this.getFinalCanvasUrl = function (cb) { var _this$state7 = _this.state, drawnImages = _this$state7.drawnImages, currentImageIndex = _this$state7.currentImageIndex; var permanentCanvas = _this.permanentCanvas.current; var canvas = _this.imageAndDrawingMixingCanvas.current; var context = canvas.getContext('2d'); context.drawImage(permanentCanvas, 0, 0); var image = new Image(); image.src = drawnImages[currentImageIndex <= 0 ? null : currentImageIndex - 1]; image.onload = function () { context.drawImage(image, 0, 0); cb(canvas.toDataURL('image/png')); }; image.crossOrigin = 'anonymous'; }; _this.copyEditorCanvasToFinalCanvas = function () { var editorCanvas = _this.editorCanvasRef.current; // upper canvas var bottomCanvas = _this.baseCanvas.current.getContext('2d'); bottomCanvas.drawImage(editorCanvas, 0, 0); }; _this.saveTheCurrentImageInstance = function () { var _this$state8 = _this.state, currentImageIndex = _this$state8.currentImageIndex, drawnImages = _this$state8.drawnImages; var imageIndex = currentImageIndex; if (currentImageIndex <= 0) imageIndex = 0; var tempDrawnImages = [].concat(toConsumableArray(drawnImages)); tempDrawnImages = tempDrawnImages.slice(0, imageIndex); var dataUrl = _this.baseCanvas.current.toDataURL('image/png'); _this.setState({ drawnImages: [].concat(toConsumableArray(tempDrawnImages), [dataUrl]), currentImageIndex: imageIndex + 1 }); }; _this.clearEditorCanvas = function () { var _this$state9 = _this.state, canvasHeight = _this$state9.canvasHeight, canvasWidth = _this$state9.canvasWidth; var editorCanvasCtx = _this.editorCanvasRef.current.getContext('2d'); editorCanvasCtx.clearRect(0, 0, canvasWidth, canvasHeight); }; _this.handleMouseUp = function (e) { var onDrawingEnd = _this.props.onDrawingEnd; // copy the drawen image to the final canvas // generate the data url of final canvas and store it in the reference array // clear the editor canvas _this.copyEditorCanvasToFinalCanvas(); _this.saveTheCurrentImageInstance(); _this.clearEditorCanvas(); _this.setState({ isMouseDown: false }); _this.getFinalCanvasUrl(function name(params) { onDrawingEnd(params); }); }; _this.handleMouseOut = function (e) { // handler for mouse out }; _this.handleImageLoad = function (e) { var imgSrc = _this.props.imgSrc; var _e$target = e.target, offsetHeight = _e$target.offsetHeight, offsetWidth = _e$target.offsetWidth; var finalBottomImageRef = _this.permanentCanvas.current.getContext('2d'); var image = new Image(); image.onload = function () { finalBottomImageRef.drawImage(image, 0, 0, offsetWidth, offsetHeight); }; // image.crossOrigin = "anonymous"; image.crossOrigin = 'anonymous'; image.src = imgSrc; _this.setState({ isImageLoaded: true, canvasHeight: offsetHeight, canvasWidth: offsetWidth }); }; _this.handleUndo = function () { // handle the logic for undo here var _this$state10 = _this.state, canvasWidth = _this$state10.canvasWidth, canvasHeight = _this$state10.canvasHeight, drawnImages = _this$state10.drawnImages, currentImageIndex = _this$state10.currentImageIndex; var onDrawingEnd = _this.props.onDrawingEnd; if (currentImageIndex <= 0) return; // this.setState({ drawingShape: null }) var oImg = new Image(); var bottomCanvas = _this.baseCanvas.current.getContext('2d'); var upperCanvas = _this.editorCanvasRef.current.getContext('2d'); bottomCanvas.globalCompositeOperation = 'source-over'; upperCanvas.globalCompositeOperation = 'source-over'; bottomCanvas.clearRect(0, 0, canvasWidth, canvasHeight); upperCanvas.clearRect(0, 0, canvasWidth, canvasHeight); oImg.onload = function () { bottomCanvas.drawImage(oImg, 0, 0); }; oImg.src = drawnImages[currentImageIndex - 2]; _this.setState({ currentImageIndex: currentImageIndex - 1 }, function () { _this.getFinalCanvasUrl(function name(params) { onDrawingEnd(params); }); }); }; _this.handleErase = function () { _this.setState({ drawingShape: 'eraser' }); }; _this.handleText = function () { _this.setState({ drawingShape: 'text', startX: null, startY: null }); }; _this.handleAddText = function (e) { var onDrawingEnd = _this.props.onDrawingEnd; var _this$state11 = _this.state, textValue = _this$state11.textValue, startX = _this$state11.startX, startY = _this$state11.startY; if (e.keyCode === 13 && !e.shiftKey) { var ctx = _this.editorCanvasRef.current.getContext('2d'); _this.fillTextOnCanvas(ctx); _this.copyEditorCanvasToFinalCanvas(); _this.saveTheCurrentImageInstance(); _this.clearEditorCanvas(); _this.setState({ textValue: '', startX: null, startY: null }); // delay, state updation is taking time here. setTimeout(function () { _this.getFinalCanvasUrl(function name(params) { onDrawingEnd(params); }); }, 1000); } }; _this.handleSelectColor = function (color) { return _this.setState({ drawingColor: color }); }; _this.editorCanvasRef = React.createRef(); _this.canvasContainerRef = React.createRef(); _this.baseCanvas = React.createRef(); _this.permanentCanvas = React.createRef(); _this.imageAndDrawingMixingCanvas = React.createRef(); _this.linkRef = React.createRef(); var configs = props.configs; var editorControls = configs && configs.length > 0 && configs.map(function (drawing) { // if role is text or color or undo var tempObj = {}; if (drawing.role === 'color') { tempObj = { onClick: null, colors: drawing.colors ? [].concat(toConsumableArray(drawing.colors)) : ['#000000'] }; } else if (drawing.role === 'text') { tempObj = { onClick: _this.handleText }; } else if (drawing.role === 'erase') { tempObj = { onClick: _this.handleErase }; } else if (drawing.role === 'undo') { tempObj = { onClick: _this.handleUndo }; } else { tempObj = { onClick: function onClick() { return _this.setState({ drawingShape: drawing.role }); } }; } return _extends({}, drawing, tempObj); }); _this.state = { startX: null, startY: null, canvasWidth: 400, canvasHeight: 400, drawingShape: 'rect', drawnImages: [], currentImageIndex: null, isImageLoaded: false, isMouseDown: false, textValue: '', lineWidth: props.lineWidth, drawingColor: '#000', screenshotControls: configs ? [].concat(toConsumableArray(editorControls)) : [] }; return _this; } // circle // draw line // drawPencil // onEraseCanvasDrawing // event handlers // handler for image loading // handle erase // add textarea createClass(Editor, [{ key: 'componentDidMount', value: function componentDidMount() { this.editorCanvasRef.current.addEventListener('mousedown', this.handleMouseDown); this.editorCanvasRef.current.addEventListener('mousemove', this.handleMouseMove); this.editorCanvasRef.current.addEventListener('mouseup', this.handleMouseUp); this.editorCanvasRef.current.addEventListener('mouseout', this.handleMouseOut); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { this.editorCanvasRef.current.removeEventListener('mousedown', this.handleMouseDown); this.editorCanvasRef.current.removeEventListener('mousemove', this.handleMouseMove); this.editorCanvasRef.current.removeEventListener('mouseup', this.handleMouseUp); this.editorCanvasRef.current.removeEventListener('mouseout', this.handleMouseOut); } }, { key: 'render', value: function render() { var _this2 = this; var editorControls = [{ icon: '╲', title: 'Move', role: 'line', onClick: function onClick() { return _this2.setState({ drawingShape: 'line' }); } }, { icon: '⊡', title: 'Square', role: 'rect', onClick: function onClick() { return _this2.setState({ drawingShape: 'rect' }); } }, { icon: '⊙', title: 'Elipse', role: 'circle', onClick: function onClick() { return _this2.setState({ drawingShape: 'circle' }); } }, { icon: '🎨', title: 'Bucket', onClick: null, colors: ['#ff6565', '#ffa000', '#cddc39', '#50e3c2', '#000', '#fff'] }, { icon: '✎', title: 'Pencil', role: 'pencil', onClick: function onClick() { return _this2.setState({ drawingShape: 'pencil' }); } }, { icon: '𝐓', title: 'Text', role: 'text', onClick: this.handleText }, { icon: '⎚', title: 'Eraser', role: 'eraser', onClick: this.handleErase }, { icon: '↺', title: 'Undo', onClick: this.handleUndo }]; var _state = this.state, isImageLoaded = _state.isImageLoaded, canvasWidth = _state.canvasWidth, canvasHeight = _state.canvasHeight, drawingShape = _state.drawingShape, startX = _state.startX, startY = _state.startY, textValue = _state.textValue, hideTextBox = _state.hideTextBox, screenshotControls = _state.screenshotControls, drawingColor = _state.drawingColor; var _props = this.props, imgSrc = _props.imgSrc, configs = _props.configs; var finalDrawingControls = configs && configs.length > 0 ? screenshotControls : editorControls; return React.createElement( 'div', { className: 'screenshotEditor_wrapper' }, React.createElement( 'div', { className: 'screenshotEditorMenu_left border-right' }, React.createElement( 'div', { className: 'screenshotEditorMenu' }, finalDrawingControls.map(function (_ref) { var icon = _ref.icon, onClick = _ref.onClick, colors = _ref.colors, role = _ref.role; return React.createElement( 'button', { key: role, className: 'screenshotEditorMenu__item ' + (role === drawingShape ? 'screenshotEditorMenu__item--active' : undefined), onClick: onClick }, configs ? React.createElement('img', { src: icon, alt: '...', className: 'screenshotEditorMenu__item__icon' }) : React.createElement( 'span', null, icon ), colors && React.createElement( 'div', { className: 'screenshotEditorMenu__item__icon__colorWrapper' }, colors.map(function (color) { return React.createElement('div', { style: { backgroundColor: color }, className: 'screenshotEditorMenu__item__icon__colorWrapper__colorBox ' + (drawingColor === color ? 'screenshotEditorMenu__item__icon__colorWrapper__colorBox--active' : ''), onClick: function onClick() { return _this2.handleSelectColor(color); } }); }) ) ); }) ) ), React.createElement( 'div', { className: 'screenshotEditorImgWrapper' }, React.createElement( 'div', { className: 'screenshotEditorArea ', ref: this.canvasContainerRef }, !isImageLoaded && React.createElement( 'span', { className: 'loading' }, 'Loading' ), React.createElement( 'div', { className: 'screenshotEditorArea_child' }, React.createElement('img', { src: imgSrc, alt: '', onLoad: this.handleImageLoad, className: 'baseScreenshotimg' }), React.createElement('canvas', { ref: this.permanentCanvas, width: canvasWidth, height: canvasHeight, className: 'screenshotEditorArea__permanentBottomcanvas' }), React.createElement('canvas', { ref: this.imageAndDrawingMixingCanvas, width: canvasWidth, height: canvasHeight, className: 'screenshotEditorArea__finalBottomcanvas' }), React.createElement('canvas', { ref: this.baseCanvas, className: 'screenshotEditorArea__canvas', width: canvasWidth, height: canvasHeight }), React.createElement('canvas', { ref: this.editorCanvasRef, style: { cursor: this.state.drawingShape == "text" ? 'text' : 'crosshair' }, className: 'screenshotEditorArea__editorCanvas', width: canvasWidth, height: canvasHeight }), drawingShape === 'text' && startX && startY && !hideTextBox && React.createElement('textarea', { className: 'screenshotEditor__text', style: { top: startY, left: startX }, value: textValue, placeholder: 'Enter text to add', onChange: function onChange(e) { return _this2.setState({ textValue: e.target.value }); }, onKeyDown: this.handleAddText }) ) ), this.props.children ) ); } }]); return Editor; }(React.Component); Editor.propTypes = { imgSrc: PropTypes.string.isRequired, onDrawingEnd: PropTypes.func.isRequired, lineWidth: PropTypes.number, configs: PropTypes.oneOfType(Array), children: PropTypes.node }; Editor.defaultProps = { lineWidth: 5, configs: null }; export default Editor; //# sourceMappingURL=index.es.js.map