diff -r de5736883786 -r f912b591e1c1 src_js/iconolab-bundle/src/components/cutout/svgboard.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src_js/iconolab-bundle/src/components/cutout/svgboard.js Fri Aug 19 19:04:26 2016 +0200 @@ -0,0 +1,576 @@ + + +import Snap from 'snapsvg' +import ShapeResizer from './shape-resizer' +import { eventEmitter } from '../utils' +import SnapsvgZoom from './snapsvg-zoom' + +/* custom plugins */ +Snap.plugin(function (Snap, Element, Paper, glob) { + var elproto = Element.prototype; + + elproto.toBack = function () { + this.prependTo(this.paper); + }; + + elproto.toFront = function () { + this.appendTo(this.paper); + }; +}); + +Element.prototype.getTransformedXY = function( x, y ) { + var m = this.transform().globalMatrix; + return { x: m.x(x,y), y: m.y(x,y) }; + }; + +var paper = null; +var mainImage = null; +var pointData = []; +var viewBoxBounds = {X: 100, Y:100}; +var zoomFactor = {x:1, y:1}; +var viewPort= {width:850, height:850}; +var currentViewBox = []; +var config = null; +var readOnly = false; +var startPoint = null; +var drawing_path = null; +var canDraw = false; +var rectZone = null; +var PATH_COLOR = "#ff00ff"; +var STROKE_COLOR = "red"; +var FILL_COLOR = "orange"; + +var SELECTED_COLOR = "#ffff00"; +var FIRST_NODE_COLOR = "#FF0000"; +var HANDLE_SIZE = 8; +var isDragged = false; +var enablePoint = true; +var pathIsClosed = false; +var ENABLE_NEW_NODE = true; +var RECT_MODE ='RECT'; +var drawingMode = RECT_MODE; //free +var FREE_MODE = 'FREE'; +var availableModes = [RECT_MODE, FREE_MODE]; +var onChangeCallback = null; + +var getId = (function () { + var cpt = 0; + var defautPrefix = "item_"; + return function (prefix) { + prefix = (typeof prefix === 'string') ? prefix : defautPrefix; + cpt = cpt + 1; + return prefix + cpt; + } + }()); + +var handleRectPath = function (path) { + if (readOnly) { + paper.path(path).attr({ stroke:'red', opacity: 0.6}); + return; + } + + var bbBox = Snap.path.getBBox(path); + rectZone = paper.rect(bbBox.x, bbBox.y, bbBox.width, bbBox.height); + rectZone.attr({fill: FILL_COLOR, stroke: STROKE_COLOR, opacity: 0.6}); + drawing_path = rectZone; + canDraw = false; + pathIsClosed = true; + ShapeResizer.enable_resizer(paper, drawing_path, viewPort, currentViewBox); +}; + + +var handleFreePath = function (path) { + + if (readOnly) { + + paper.path(path).attr({ + stroke: 'orange', + fill: 'orange', + opacity: 0.5, + }); + + return; + } + + var pathInfos = Snap.parsePathString(path); + pathInfos.map(function (pathData) { + if(pathData[0] !== 'Z') { + createPoint(paper, pathData[1], pathData[2], pointData); + } else { + pathIsClosed = true; + updatePath(paper, onClosePath); + } + }); + + /* replay the path here */ + +}; +//transform point to path +var updatePath = function (paper, updateCallback) { + var path = "M"; + + if (pointData.length <= 1) { + return; + } + + path += pointData[0].x + ',' + pointData[0].y; + + for (var i=0; i < pointData.length; i++) { + if (i == 0) continue; + + var pointInfos = pointData[i]; + var lPath = "L" + pointInfos.x + "," + pointInfos.y; + path += " " + lPath; + } + + path += (pathIsClosed) ? " Z": ""; + + /* remove prev path */ + if (drawing_path) { + drawing_path.remove(); + } + + drawing_path = paper.path(path); + + drawing_path.attr({ + stroke: STROKE_COLOR, + "vector-effect": "non-scaling-stroke",//prevent line to be zoom in + "stroke-width": 3, + fill: "white", + opacity: 0.1 + }); + + /* bring all handler to front */ + pointData.map(function (point) { + + /*deal with handler size */ + var handleSize = computeHandleSize(); + if (point.handler) { + point.handler.toFront(); + } + }); + + if (typeof updateCallback === 'function' && pathIsClosed) { + updateCallback(); + } + + if (!updateCallback && pathIsClosed) { + applyClosedStyle(); + } +}; + +var applyClosedStyle = function () { + drawing_path.attr({ fill: FILL_COLOR, strokeWidth: 1, opacity:.6 }); +} + +var onClosePath = function () { + ENABLE_NEW_NODE = false; + applyClosedStyle(); +}; + + +var onClickOnHandler = function (point, p, e) { + //close path + if (point.isFirst && pointData.length > 2) { + pathIsClosed = true; + } +}; + +var updatePointPosition = function (newPoint, x, y) { + var index = pointData.indexOf(newPoint); + if (index !== -1) { + pointData[index].x = x; + pointData[index].y = y; + return true; + } else { + return false; + } +}; + +var clearPreviousPath = function () { + drawing_path.remove(); +}; + +var computeHandleSize = function () { + + if(!currentViewBox.length) { + currentViewBox = [0, 0, parseInt(mainImage.width()), parseInt(mainImage.height())] + } + var currentHandleSize = HANDLE_SIZE * Math.min(currentViewBox[2], currentViewBox[3]) / 850; + return currentHandleSize; +} + +var onMoveHandler = function (dx, dy, posX, posY, e) { + isDragged = true; + var tdx, tdy; + var snapInvMatrix = this.transform().diffMatrix.invert(); + snapInvMatrix.e = snapInvMatrix.f = 0; + tdx = snapInvMatrix.x( dx,dy ); + tdy = snapInvMatrix.y( dx,dy ); + var transformValue = this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [tdx, tdy]; + this.attr({ transform: transformValue}); + var boxSize = this.getBBox(); + + var wasUpdated = updatePointPosition(this.data('point'), boxSize.x + (computeHandleSize() / 2) , boxSize.y + (computeHandleSize() / 2)); + + if (wasUpdated) { + updatePath(this.paper); + } +} + +var bindHandlerEvent = function (point, p) { + point.handler.click(onClickOnHandler.bind(this, point, p)); + /* -- handler -- */ + point.handler.hover(function () { + point.handler.attr({fill: 'yellow'}); + }, function () { + var fillColor = point.isFirst ? FIRST_NODE_COLOR : ""; + point.handler.attr({fill: fillColor}); + }); + + point.handler.drag(onMoveHandler, function () { + this.data('origTransform', this.transform().local ); + }, function () { + if (!isDragged) { return true; } + isDragged = false; + enablePoint = false; + }); +} + +var createPointHandler = function (p, point) { + var handler; + var handleSize = computeHandleSize(); + var handleX = point.x - handleSize / 2; + var handleY = point.y - handleSize / 2; + + /* preserve initial size of 5px a quoi correspond 5 deal with current vp */ + handler = p.rect(handleX, handleY, handleSize, handleSize); + + handler.addClass("drawingHandler"); + point.handler = handler; + point.handler.data('point', point); + if (pointData.length === 0) { + point.isFirst = true; + } + + bindHandlerEvent(point, p); + point.handler.attr({ + fill: (pointData.length === 0) ? FIRST_NODE_COLOR : "", + opacity: 0.9, + stroke: PATH_COLOR + }); + + return point; +} + +//create paper +var createPoint = function (paper, x, y, pointData) { + + var point = {x:x, y:y, id: getId()}; + + if (pathIsClosed) { + updatePath(paper, onClosePath); + return; + } + + if (!enablePoint) { + enablePoint = true; + return false; + } + + point = createPointHandler(paper, point); + pointData.push(point); + updatePath(paper); +}; + +var attachRectEvents = function (paper) { + if (readOnly) { return false; } + + var startPosition = {}; + var currentPosition = {}; + /* add resizer */ + + paper.mousedown(function (e) { + + if (drawingMode === FREE_MODE || pathIsClosed) { return; } + startPosition.x = e.offsetX; + startPosition.y = e.offsetY; + canDraw = true; + }); + + paper.mousemove(function (e) { + if (drawingMode === FREE_MODE) { return; } + if (!canDraw) { return; } + var x, y; + currentPosition.x = e.offsetX; + currentPosition.y = e.offsetY; + + if (rectZone) { + rectZone.remove(); + } + + /* bas -> droite */ + var width = Math.abs(currentPosition.x - startPosition.x); + var height = Math.abs(startPosition.y - currentPosition.y); + + if (currentPosition.y > startPosition.y && currentPosition.x > startPosition.x) { + x = startPosition.x; + y = startPosition.y; + } + + /* haut -> droite */ + if (currentPosition.y < startPosition.y && currentPosition.x > startPosition.x) { + x = currentPosition.x - width; + y = currentPosition.y; + } + + /* haut -> gauche */ + if (currentPosition.y < startPosition.y && currentPosition.x < startPosition.x) { + x = currentPosition.x; + y = currentPosition.y; + } + + /* bas -> gauche */ + if (currentPosition.y > startPosition.y && currentPosition.x < startPosition.x) { + x = currentPosition.x + y = currentPosition.y - height; + } + if(!x || !y) { return; } + + rectZone = paper.rect(x, y, width, height); + rectZone.attr({fill: FILL_COLOR, stroke: STROKE_COLOR, opacity: 0.6}); + }); + + + paper.mouseup(function () { + if ((drawingMode === FREE_MODE) || pathIsClosed || !rectZone) { return false; } + drawing_path = rectZone; + ShapeResizer.enable_resizer(paper, rectZone, viewPort, currentViewBox); + canDraw = false; + pathIsClosed = true; + }); +}; +var attachPointEvents = function (paper) { + if (readOnly) { return; } + paper.click( function(e) { + if (drawingMode === RECT_MODE) { + return true; + } + + if (!ENABLE_NEW_NODE) { return true; } + createPoint(paper, e.offsetX, e.offsetY, pointData); + }); +}; + + +var attachZoomEvents = function () { + + eventEmitter.on("zoomChanged", function (zoomInfos) { + zoomFactor = zoomInfos.zoomFactor; + currentViewBox = zoomInfos.currentViewBox; + var previousPath = API.getPath(); + API.clear(); + API.setPath(previousPath); + }); + +} + + +var API = { + + getPaper: function () { + return paper; + }, + + setPath: function (pathString) { + /* redraw the path */ + var pathInfos = pathString.split(';'); + if ( availableModes.indexOf(pathInfos[1]) === -1) { + /* We assume then it is a free path */ + pathInfos[1] = "FREE"; + } + + this.setDrawingMode(pathInfos[1]); + var pathData = pathInfos[0]; + + if (!pathData.length) { + return; + } + /* deal with path nomalization x = ImageWith/MaxXBound */ + var xRatio = mainImage.attr("width") / viewBoxBounds.X; + var yRatio = mainImage.attr("height") / viewBoxBounds.Y; + + if (isNaN(xRatio) || isNaN(yRatio)) { + new Error('Ratio should be a number.'); + } + + var transformMatrix = Snap.matrix(xRatio, 0, 0, yRatio, 0, 0); + var path = Snap.path.map(pathData, transformMatrix).toString(); + + /* always close path */ + if (path.search(/[z|Z]/gi) === -1 ) { + path += "Z"; + } + if (pathInfos.length >= 2) { + if (pathInfos[1] === RECT_MODE) { + handleRectPath(path); + } + + if (pathInfos[1] === FREE_MODE) { + handleFreePath(path); + } + } + }, + + setDrawingMode: function (mode) { + + if (availableModes.indexOf(mode) !== -1) { + drawingMode = mode; + } + if (typeof onChangeCallback === "function") { + onChangeCallback(drawingMode); + } + + this.clear(); + }, + + clear: function () { + /* clear previous path, point, handler */ + pointData.map(function (point) { + if (point.handler) { + point.handler.remove(); + } + }); + + /*clear path is exists*/ + if (drawing_path) { + drawing_path.remove(); + } + eventEmitter.emit("cutout:clear"); + pointData = []; + startPoint = null; + drawing_path = null; + isDragged = false; + enablePoint = true; + pathIsClosed = false; + ENABLE_NEW_NODE = true; + }, + + getShapeBBox: function () { + var currentPath = this.getPath(); + return Snap.path.getBBox(currentPath); + }, + + getShape: function () { + return this.getPath(); + }, + + getPath: function () { + /* retourne le chemin */ + /* send path and BBox | implement edit and load path */ + var path = ""; + if (drawing_path) { + if (drawingMode === RECT_MODE) { + var bBox = drawing_path.getBBox(); + var transform = drawing_path.transform(); + + if (!transform.global.length) { + var shapePath = drawing_path.getBBox().path; + } + + else { + + var shapeX = drawing_path.node.getAttribute('x'); + var shapeY = drawing_path.node.getAttribute('y'); + var transformMatrix = transform.totalMatrix; + var fakeShape = paper.rect(transformMatrix.x(shapeX, shapeY),transformMatrix.y(shapeX, shapeY), bBox.width, bBox.height); + shapePath = fakeShape.getBBox().path; + fakeShape.remove(); + } + + path = Snap.path.toAbsolute(shapePath).toString(); + + } + else { + path = drawing_path.attr('d'); + } + } + + var xRatio = viewBoxBounds.X / mainImage.attr("width"); + var yRatio = viewBoxBounds.Y / mainImage.attr("height"); + + if(isNaN(xRatio) || isNaN(yRatio)) { + new Error('ratio should be a number.'); + } + + if (!path.length) { + path = (drawingMode === RECT_MODE) ? ";RECT" : ";FREE"; + return path; + } + var normalizeMatrix = Snap.matrix(xRatio, 0, 0, yRatio, 0, 0); + + path = Snap.path.map(path, normalizeMatrix).toString(); + + /* save the type */ + var type = (drawingMode === RECT_MODE) ? ";RECT" : ";FREE"; + if (path.search(/[z|Z]/gi) === -1) { + path += " Z"; + } + + path += type; + + + return path; + } +}; + +/* change to a component */ +export default { + + init: function(config) { + mainImage = jQuery(config.wrapperId).find('.main-image').eq(0); + var cutCanvas = jQuery(config.wrapperId).find('.cut-canvas').eq(0); + var path = jQuery(config.wrapperId).find('.image-path').eq(0); + + if (typeof config.onDrawingModeChange === 'function') { + onChangeCallback = config.onDrawingModeChange; + } + + if (!mainImage.length) { + throw new Error("The main image Can't be found ..."); + } + + if (!cutCanvas.length) { + var cutCanvas = jQuery('').addClass('cut-canvas'); + jQuery(config.wrapperId).append(cutCanvas); + cutCanvas.append(mainImage); + } + + + + cutCanvas.css({ + marginLeft: 'auto', + marginRight: 'auto', + width: viewPort.width, + height: viewPort.height, + }); + if (typeof config.readOnly === 'boolean' && config.readOnly === true) { + readOnly = true; + } + + paper = new Snap(cutCanvas.get(0)); + + if (path.length) { + jQuery(cutCanvas).append(path); + var pathData = path.attr("d"); + API.setPath(pathData); + path.remove(); + } + /* enable zoom */ + attachZoomEvents(); + attachPointEvents(paper); + attachRectEvents(paper); + + return API; + } +}; \ No newline at end of file