A Pen by Lingjia Liu on CodePen.
Created
August 24, 2018 01:08
-
-
Save mutoo/ab286334e22208d1982a55f866fda8fc to your computer and use it in GitHub Desktop.
Svg Path Inspector
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <div id="app"> | |
| <div ref="canvas"> | |
| </div> | |
| <div class="caption"> | |
| {{pathString}} | |
| </div> | |
| </div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // This pen demonstrate how svg path works | |
| const SVG_PATH = `M40 60L60 140C100 240 100 80 140 60S220 100 160 220Q260 240 300 140T340 260`; | |
| new Vue({ | |
| el: '#app', | |
| data() { | |
| return { | |
| pathString: '', | |
| hammers_: [], | |
| }; | |
| }, | |
| methods: { | |
| setup(width, height) { | |
| this.canvas = SVG(this.$refs['canvas']).size(width, height); | |
| this.createStage(400, 300); | |
| this.createGrid(20); | |
| this.createFrame(); | |
| this.createDefs(); | |
| this.createMovingHandle(); | |
| this.createPathWithHandles(); | |
| this.translate(120, 90); // center the stage | |
| }, | |
| createStage(width, height) { | |
| this.stage = this.canvas.group(); | |
| this.stage.rect(width, height). | |
| fill('white'). | |
| stroke({width: 1}). | |
| addClass('stage'); | |
| this.canvas.add(this.stage); | |
| }, | |
| createGrid(gridSize) { | |
| this.gridPattern = this.canvas.pattern(gridSize, gridSize, add => { | |
| add.polyline([0, gridSize, 0, 0, gridSize, 0]). | |
| fill('none'). | |
| stroke({width: 1, opacity: 0.25}); | |
| }); | |
| this.grid = this.canvas.rect(this.canvas.width(), | |
| this.canvas.height()). | |
| fill(this.gridPattern.fill()). | |
| addClass('grid'); | |
| }, | |
| createFrame() { | |
| this.canvas.rect(640, 480). | |
| fill('none'). | |
| stroke({width: 1}). | |
| addClass('frame'); | |
| }, | |
| createDefs() { | |
| let defs = this.canvas.defs(); | |
| this.rectHandleDef = defs.rect(10, 10).stroke({ | |
| width: 1, | |
| }).translate(-5, -5); | |
| this.circleHandleDef = defs.circle(10).stroke({ | |
| width: 1, | |
| }).translate(-5, -5); | |
| }, | |
| translate(x, y) { | |
| this.gridPattern.translate( | |
| x % this.gridPattern.width(), | |
| y % this.gridPattern.height(), | |
| ); | |
| this.stage.translate(x, y); | |
| }, | |
| createMovingHandle() { | |
| let movingHandle = this.stage.use(this.rectHandleDef). | |
| center(390, 10). | |
| addClass('handle'). | |
| addClass('handle--movable'); | |
| let hammer = this.createHammer(movingHandle.node); | |
| let panstartTX; | |
| let panstartTY; | |
| hammer.on('panstart', ev => { | |
| let transform = this.stage.transform(); | |
| panstartTX = transform.x; | |
| panstartTY = transform.y; | |
| }); | |
| hammer.on('panmove', ev => { | |
| this.translate(panstartTX + ev.deltaX, panstartTY + ev.deltaY); | |
| }); | |
| }, | |
| createPathWithHandles() { | |
| this.path = this.stage.path(SVG_PATH). | |
| fill('none'). | |
| stroke({width: 2}); | |
| // console.log(this.path.array()); | |
| let pathArray = this.path.array(); | |
| this.pathString = pathArray.toString(); | |
| let handlesLayer = this.stage.group(); | |
| let gizmo = handlesLayer.group(); | |
| let updateGizmo = () => { | |
| gizmo.clear(); | |
| let lastSecondPoint; | |
| let lastPoint; | |
| let ghostPoint; | |
| for (let i = 0; i < pathArray.value.length; i++) { | |
| let component = pathArray.value[i]; | |
| switch (component[0]) { | |
| case 'M': | |
| lastSecondPoint = null; | |
| lastPoint = [component[1], component[2]]; | |
| break; | |
| case 'L': | |
| lastSecondPoint = lastPoint; | |
| lastPoint = [component[1], component[2]]; | |
| break; | |
| case 'C': | |
| gizmo.line( | |
| lastPoint[0], | |
| lastPoint[1], | |
| component[1], | |
| component[2], | |
| ).stroke({ | |
| width: 1, | |
| }).addClass('gizmo-line'); | |
| gizmo.line( | |
| component[3], | |
| component[4], | |
| component[5], | |
| component[6], | |
| ).stroke({ | |
| width: 1, | |
| }).addClass('gizmo-line'); | |
| lastSecondPoint = [component[3], component[4]]; | |
| lastPoint = [component[5], component[6]]; | |
| break; | |
| case 'S': | |
| if (lastSecondPoint) { | |
| ghostPoint = [ | |
| 2 * lastPoint[0] - lastSecondPoint[0], | |
| 2 * lastPoint[1] - lastSecondPoint[1], | |
| ]; | |
| gizmo.line( | |
| lastPoint[0], | |
| lastPoint[1], | |
| ghostPoint[0], | |
| ghostPoint[1], | |
| ).stroke({ | |
| width: 1, | |
| }).addClass('gizmo-line'); | |
| } | |
| gizmo.line( | |
| component[1], | |
| component[2], | |
| component[3], | |
| component[4], | |
| ).stroke({ | |
| width: 1, | |
| }).addClass('gizmo-line'); | |
| if (lastSecondPoint) { | |
| gizmo.use(this.rectHandleDef). | |
| center(ghostPoint[0], ghostPoint[1]). | |
| addClass('handle--kinect'); | |
| } | |
| lastSecondPoint = [component[1], component[2]]; | |
| lastPoint = [component[3], component[4]]; | |
| break; | |
| case 'Q': | |
| gizmo.line( | |
| lastPoint[0], | |
| lastPoint[1], | |
| component[1], | |
| component[2], | |
| ).stroke({ | |
| width: 1, | |
| }).addClass('gizmo-line'); | |
| gizmo.line( | |
| component[1], | |
| component[2], | |
| component[3], | |
| component[4], | |
| ).stroke({ | |
| width: 1, | |
| }).addClass('gizmo-line'); | |
| lastSecondPoint = [component[1], component[2]]; | |
| lastPoint = [component[3], component[4]]; | |
| break; | |
| case 'T': | |
| if (!lastSecondPoint) { | |
| lastSecondPoint = lastPoint; | |
| } | |
| ghostPoint = [ | |
| 2 * lastPoint[0] - lastSecondPoint[0], | |
| 2 * lastPoint[1] - lastSecondPoint[1], | |
| ]; | |
| gizmo.line( | |
| lastPoint[0], | |
| lastPoint[1], | |
| ghostPoint[0], | |
| ghostPoint[1], | |
| ).stroke({ | |
| width: 1, | |
| }).addClass('gizmo-line'); | |
| gizmo.line( | |
| ghostPoint[0], | |
| ghostPoint[1], | |
| component[1], | |
| component[2], | |
| ).stroke({ | |
| width: 1, | |
| }).addClass('gizmo-line'); | |
| gizmo.use(this.rectHandleDef). | |
| center(ghostPoint[0], ghostPoint[1]). | |
| addClass('handle--kinect'); | |
| lastSecondPoint = [ghostPoint[0], ghostPoint[1]]; | |
| lastPoint = [component[1], component[2]]; | |
| break; | |
| } | |
| } | |
| }; | |
| let updatePath = () => { | |
| updateGizmo(); | |
| this.path.plot(pathArray); | |
| this.pathString = pathArray.toString(); | |
| }; | |
| updateGizmo(); | |
| pathArray.value.forEach(component => { | |
| switch (component[0]) { | |
| case 'M': | |
| case 'L': | |
| this.createHandleForPoint( | |
| handlesLayer, | |
| this.circleHandleDef, | |
| component, | |
| 1, | |
| 2, | |
| updatePath, | |
| ); | |
| break; | |
| case 'C': | |
| this.createHandleForPoint( | |
| handlesLayer, | |
| this.rectHandleDef, | |
| component, | |
| 1, | |
| 2, | |
| updatePath, | |
| ); | |
| this.createHandleForPoint( | |
| handlesLayer, | |
| this.rectHandleDef, | |
| component, | |
| 3, | |
| 4, | |
| updatePath, | |
| ); | |
| this.createHandleForPoint( | |
| handlesLayer, | |
| this.circleHandleDef, | |
| component, | |
| 5, | |
| 6, | |
| updatePath, | |
| ); | |
| break; | |
| case 'S': | |
| case 'Q': | |
| this.createHandleForPoint( | |
| handlesLayer, | |
| this.rectHandleDef, | |
| component, | |
| 1, | |
| 2, | |
| updatePath, | |
| ); | |
| this.createHandleForPoint( | |
| handlesLayer, | |
| this.circleHandleDef, | |
| component, | |
| 3, | |
| 4, | |
| updatePath, | |
| ); | |
| break; | |
| case 'T': | |
| this.createHandleForPoint( | |
| handlesLayer, | |
| this.circleHandleDef, | |
| component, | |
| 1, | |
| 2, | |
| updatePath, | |
| ); | |
| break; | |
| } | |
| }); | |
| }, | |
| createHandleForPoint( | |
| container, | |
| def, | |
| component, | |
| xIdx, | |
| yIdx, | |
| updateCallback, | |
| ) { | |
| let movingHandle = container.use(def). | |
| center(component[xIdx], component[yIdx]). | |
| addClass('handle'). | |
| addClass('handle--movable'); | |
| let hammer = this.createHammer(movingHandle.node); | |
| let panstartX; | |
| let panstartY; | |
| hammer.on('panstart', ev => { | |
| panstartX = movingHandle.x(); | |
| panstartY = movingHandle.y(); | |
| }); | |
| hammer.on('panmove', ev => { | |
| component[xIdx] = this.snapToGrid(panstartX + ev.deltaX); | |
| component[yIdx] = this.snapToGrid(panstartY + ev.deltaY); | |
| movingHandle.center(component[xIdx], component[yIdx]); | |
| updateCallback(); | |
| }); | |
| }, | |
| snapToGrid(n) { | |
| let gridSize = this.gridPattern.width(); | |
| return Math.round(n / gridSize) * gridSize; | |
| }, | |
| createHammer(node) { | |
| let hammer = new Hammer(node); | |
| this.hammers_.push(hammer); | |
| return hammer; | |
| }, | |
| destroyHammers() { | |
| this.hammers_.forEach(hammer => { | |
| hammer.destroy(); | |
| }); | |
| }, | |
| }, | |
| mounted() { | |
| this.setup(640, 480); | |
| }, | |
| beforeDestroy() { | |
| this.destroyHammers(); | |
| }, | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <script src="//unpkg.com/vue"></script> | |
| <script src="//unpkg.com/svg.js"></script> | |
| <script src="//unpkg.com/hammerjs"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| .container { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| } | |
| .caption { | |
| width: 640px; | |
| padding: 20px; | |
| box-sizing: border-box; | |
| background-color: #efefef; | |
| font-family: 'Source Code Pro', Menlo, Consolas, Monaco, monospace; | |
| } | |
| .grid { | |
| pointer-events: none; | |
| } | |
| .handle { | |
| cursor: pointer; | |
| fill: white; | |
| } | |
| .handle--movable { | |
| cursor: move; | |
| } | |
| .handle--kinect { | |
| cursor: no-drop; | |
| fill: #999999; | |
| } | |
| .gizmo-line { | |
| opacity: 0.5; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment