The Shape Editor sample application demonstrates how you can create and edit various shapes using SVG (Scalable Vector Graphics).
The following figure illustrates the main screens of the Shape Editor.
Figure: Shape Editor screens
The application opens with a blank SVG canvas with the action buttons at the bottom:
- Click Shape to select a specific shape and the size of the shape.
- Click Color to select the color of the shape from a color picker.
- Click Clear to remove all the shapes on the SVG canvas.
Source Files
You can create and view the sample application project including the source files in the IDE.
File name | Description |
---|---|
config.xml | This file contains the application information for the platform to install and launch the application. |
css/style.css | This file contains the CSS styling for the application UI. |
index.html | This is a starting file from which the application starts loading. It contains the layout of the application screens. |
js/app.js | This file contains the code for handling the main functionality of the application. |
Implementation
Use graphic elements to create shapes in the SVG board. Attributes, including size and color, of the shape can be defined in the tag.
<--! index.html --> <svg class="shape-svg" viewBox="0 0 50 50"> <rect class="shape-element" id="select-rect" width="50" height="50" /> </svg> <svg class="shape-svg" viewBox="0 0 50 50"> <rect class="shape-element" id="select-rectrnd" rx="17.5" ry="17.5" width="50" height="50" /> </svg> <svg class="shape-svg" viewBox="0 0 50 50"> <ellipse class="shape-element" id="select-ellipse" cx="25" cy="25" rx="25" ry="25" /> </svg> <svg class="shape-svg" viewBox="0 0 50 50"> <polygon class="shape-element" id="select-triangle" points="25,0 0,50 50,50" /> </svg> <svg class="shape-svg" viewBox="0 0 50 50"> <polygon class="shape-element" id="select-star" points="25,0 10,50 50,19 0,19 40,50" /> </svg>
Since SVG elements are graphics defined in XML format, the innerHtml property is not supported with the SVG elements.
A workaround method is required for inserting new SVG elements into the SVG board dynamically using JavaScript.
/* js/app.js */ function insertSvgShape(boardId, elemId, shapeId, shapeSize, translateX, translateY) { var tempDiv = document.createElement("div"), // Create a dummy div element svgFragment = "", shapeTag = "", colorTag = "", tempTag = ""; // Construct the tag for the new shape element switch (shapeId) { case "select-rect": shapeTag = SVG_TAG_RECT; break; case "select-rectrnd": shapeTag = SVG_TAG_RECTRND; break; case "select-ellipse": shapeTag = SVG_TAG_ELLIPSE; break; case "select-triangle": shapeTag = SVG_TAG_TRIANGLE; break; case "select-star": shapeTag = SVG_TAG_STAR; break; } // Add the color information to the tag colorTag = "style='fill:" + customColor + ";"; shapeTag = shapeTag.replace("style='", colorTag); // Add the size and location information as well as the id information to the tag tempTag = "transform='matrix(" + shapeSize + " 0 0 " + shapeSize + " " + translateX + " " + translateY + ")' id='" + elemId + "'/>"; shapeTag = shapeTag.replace(" />", tempTag); // Wrap the SVG string as an SVG object svgFragment = "<svg>" + shapeTag + "</svg>"; // Add the SVG object to the temporary div element tempDiv.innerHTML = svgFragment; // Use Array.prototype.slice() method to copy items of tempDiv to the actual SVG board Array.prototype.slice.call(tempDiv.childNodes[0].childNodes).forEach(function(el) { document.querySelector("#" + boardId).appendChild(el); if (boardId === "svg-board") { el.addEventListener("touchmove", touchMoveHandler, false); } }); // Update the index so that it can be used for the next shape to be added customIndex++; }
Handle touch events:
-
The touchStartHandler() method is called when the touchstart event is triggered. It uses the changedTouches array event property to access the data of the current touch object. The current touch data is saved to the drawPath object, which is an associative array indexed with the identifier parameter of the current touch object.
/* js/app.js */ function touchStartHandler(event) { var touch = event.changedTouches[0]; // Store information about the first touch point in the drawPath array drawPath[touch.identifier] = touch; }
-
The touchMoveHandler() method is called when the touchmove event is triggered on the SVG elements. It moves the element around the board following the path of the touch points.
/* js/app.js */ function touchMoveHandler(event) { var i = 0, j = 0, x = 0, y = 0, touches = event.changedTouches, currentDrawPath = null, originalSize = 0, originalTransform = "", newTransform = "", transformArray = null; // While moving a shape, the color of the shape becomes translucent event.target.style.opacity = 0.5; // Move shape around following the path of the touch points for (i = 0; i < touches.length; i += 1) { currentDrawPath = drawPath[touches[i].identifier]; if (currentDrawPath !== undefined) { // Get a string about the original transformation data of the shape object originalTransform = event.target.getAttribute("transform"); // Split the string and insert each data into a temporary array so that each item can be accessed transformArray = originalTransform.split(" "); // Get size of the shape originalSize = Number(transformArray[3]); // Calculate x and y coordinates of the current touch point x = currentDrawPath.pageX - svgContainer.offsetLeft - (originalSize * 25); y = currentDrawPath.pageY - svgContainer.offsetTop - (originalSize * 25); // Insert the new coordinate data to the transformArray transformArray[4] = x; transformArray[5] = y + ")"; // Create a string using the new data for (j = 0; j < transformArray.length; j++) { newTransform += transformArray[j] + " "; } // Change the transformation data of the shape object with the new string event.target.setAttribute("transform", newTransform); drawPath[touches[i].identifier] = touches[i]; } } // Indicate that a shape is being moved (not inserting a new shape) isMoving = 1; event.preventDefault(); }
-
The touchEndHandler() method is called when the touchend event is triggered, and it adds a new shape into the SVG board. The pageX and pageY properties of the touch object represent the coordinates of the touch point. After a new shape is inserted, the method resets the drawPath object to remove the information about the touch that no longer exists.
/* js/app.js */ function touchEndHandler(event) { var i = 0, x = 0, y = 0, touch = event.changedTouches[0], originalSize = 0, originalTransform = "", newTransform = "", transformArray = null; // If not moving a shape, insert a new shape to the SVG board if (!isMoving) { // Get x and y coordinates of the final touch point x = touch.pageX - svgContainer.offsetLeft - (customSize * 25); y = touch.pageY - svgContainer.offsetTop - (customSize * 25); insertSvgShape("svg-board", "shape" + customIndex, customShape, customSize, x, y); delete drawPath[touch.identifier]; } // If moving an existing shape, locate the shape to a new point at the end else { // Get a string about the original transformation data of the shape object originalTransform = event.target.getAttribute("transform"); // Split the string and insert each data into a temporary array so that each item can be accessed transformArray = originalTransform.split(" "); // Get size of the shape originalSize = Number(transformArray[3]); // Calculate x and y coordinates of the final touch point x = touch.pageX - svgContainer.offsetLeft - (originalSize * 25); y = touch.pageY - svgContainer.offsetTop - (originalSize * 25); // At the end of moving a shape, restore the color opacity to the original state event.target.style.opacity = 1; // Insert the new coordinate data to the transformArray transformArray[4] = x; transformArray[5] = y + ")"; // Create a string using the new data for (i = 0; i < transformArray.length; i++) { newTransform += transformArray[i] + " "; } // Change the transformation data of the shape object with the new string event.target.setAttribute("transform", newTransform); delete drawPath[touch.identifier]; // Set the flag back to the initial state isMoving = 0; } }