D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
hnakamur
Full window
Github gist
offseted marker on cubic bezier curve example
<!DOCTYPE html> <html> <head> <title>Cubic bezier curve length</title> <style> #figure1 { border: 1px solid black; } .draggable { cursor: move; } .control-point { opacity: 0.5; stroke: none; fill: red; } .split-point { opacity: 0.5; stroke: none; fill: purple; } text { font-size: 12px; } </style> </head> <body> <svg id="figure1" width="400" height="400"> <defs> <marker id="arrowhead" viewBox="0 0 10 10" refX="40" refY="5" markerWidth="10" markerHeight="10" orient="auto"> <path d="M10 5 0 10 0 8.7 6.8 5.5 0 5.5 0 4.5 6.8 4.5 0 1.3 0 0Z"/> </marker> </defs> <path d="M120 160 35 200 220 260 220 40" stroke="black" fill="none" id="controlPath"/> <path d="M120 160C35 200 220 260 220 40" stroke="green" fill="none" id="curve" marker-end="url(#arrowhead)"/> <circle cx="120" cy="160" r="8" id="p1" class="control-point draggable"/> <circle cx="35" cy="200" r="8" id="p2" class="control-point draggable"/> <circle cx="220" cy="260" r="8" id="p3" class="control-point draggable"/> <circle cx="220" cy="40" r="30" id="p4" class="control-point draggable"/> <circle cx="-10" cy="-10" r="8" id="splitPoint" class="split-point"/> <text x="20" y="20" id="curveLength">curve length: 0</text> <text x="120" y="160" dx="10" id="t1">p1: 120/160</text> <text x="35" y="200" dx="10" id="t2">p2: 35/200</text> <text x="220" y="260" dx="10" id="t3">p3: 220/260</text> <text x="220" y="40" dx="10" id="t4">p4: 220/40</text> </svg> <script src="bezier-curve.js"></script> <script> // This is a port to JavaScript from [Arc length | A Primer on Bézier Curves]( https://pomax.github.io/bezierinfo/#arclength ) // MIT license var controlPath = document.getElementById('controlPath'); var curve = document.getElementById('curve'); var p1 = document.getElementById('p1'); var p2 = document.getElementById('p2'); var p3 = document.getElementById('p3'); var p4 = document.getElementById('p4'); var dragElem = null; var svg = document.getElementById('figure1'); svg.addEventListener('mousemove', onMouseMove); svg.addEventListener('mouseup', onMouseUp); var elements = [p1, p2, p3, p4]; for (i = 0; i < elements.length; i++) { elements[i].addEventListener('mousedown', onMouseDown); } var mouseOffsetX, mouseOffsetY; function getClientPointInSVG(ev) { var p, m; p = svg.createSVGPoint(); p.x = ev.clientX; p.y = ev.clientY; m = dragElem.getScreenCTM(); return p.matrixTransform(m.inverse()); } function onMouseDown(ev) { var p; dragElem = ev.target; p = getClientPointInSVG(ev); mouseOffsetX = p.x - dragElem.getAttribute('cx'); mouseOffsetY = p.y - dragElem.getAttribute('cy'); } function onMouseMove(ev) { var p, x, y, circleID, circleTextID, circleTextElem; if (!dragElem) { return; } p = getClientPointInSVG(ev); x = p.x - mouseOffsetX; y = p.y - mouseOffsetY; dragElem.setAttribute('cx', x); dragElem.setAttribute('cy', y); controlPath.setAttribute('d', 'M' + p1.getAttribute('cx') + ' ' + p1.getAttribute('cy') + ' ' + p2.getAttribute('cx') + ' ' + p2.getAttribute('cy') + ' ' + p3.getAttribute('cx') + ' ' + p3.getAttribute('cy') + ' ' + p4.getAttribute('cx') + ' ' + p4.getAttribute('cy')); curve.setAttribute('d', 'M' + p1.getAttribute('cx') + ' ' + p1.getAttribute('cy') + 'C' + p2.getAttribute('cx') + ' ' + p2.getAttribute('cy') + ' ' + p3.getAttribute('cx') + ' ' + p3.getAttribute('cy') + ' ' + p4.getAttribute('cx') + ' ' + p4.getAttribute('cy')); circleID = dragElem.getAttribute('id'); circleTextID = circleID.replace(/^p/, 't'); circleTextElem = document.getElementById(circleTextID); circleTextElem.setAttribute('x', x); circleTextElem.setAttribute('y', y); circleTextElem.firstChild.data = circleID + ': ' + x + '/' + y; updateCurveLengthText(); getParameterForTrim(); } function onMouseUp(ev) { dragElem = null; } function updateCurveLengthText() { var textElem = document.getElementById('curveLength'); var curve = new BezierCurve(elements.map(function(elem) { return {x: +elem.getAttribute('cx'), y: +elem.getAttribute('cy')}; })); var arcLength = curve.getArcLength(); textElem.firstChild.data = 'curve length: ' + arcLength; } updateCurveLengthText(); function getParameterForTrim() { var curve = new BezierCurve(elements.map(function(elem) { return {x: +elem.getAttribute('cx'), y: +elem.getAttribute('cy')}; })); var trimLen = 30; var pathTotalLength = curve.getArcLength(); var s = pathTotalLength - trimLen; var t = curve.getParameterAtArcLength(s); var pathLen = curve.getArcLength(t); var p = curve.getPointAtParameter(t); console.log('t', t, 'pathLen', pathLen, 'diff', (pathTotalLength - pathLen)); var splitPointCircleElem = document.getElementById('splitPoint'); splitPointCircleElem.setAttribute('cx', p.x); splitPointCircleElem.setAttribute('cy', p.y); } getParameterForTrim(); </script> </body> </html>