js+svg实现可配置修改参数的麻绳打结动画效果代码
代码语言:html
所属分类:动画
代码描述:js+svg实现可配置修改参数的麻绳打结动画效果代码
代码标签: js svg 配置 修改 参数 麻绳 打结 动画
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <style> * { margin: 0; padding: 0; } body { padding: 50px 20px; font-family: Helvetica, Arial, sans-serif; font-size: 16px; } input { accent-color: #4394e5; } a, a:visited { display: inline-block; font-size: 20px; line-height: 1; margin: 20px 0; color: black; border-bottom: 1px solid #ccc; outline-offset: 5px; transition: all 250ms; text-decoration: none; } a:hover, a:visited:hover { border-bottom-color: #4394e5; } a:active, a:visited:active { transform: translateY(1px); } .demo { max-width: 1000px; margin: 0 auto; padding: 40px 20px; } .demo-svg { overflow: visible; } path { stroke-width: 1.5; } .path { stroke: #4394e5; } .normals path { stroke: #4394e5; } .points { stroke: none; fill: #4394e5; } .segment path, .polygons-rounded path, .polygons path { stroke: #4394e5; } .demo-controls { display: flex; flex-wrap: wrap; max-width: 750px; } .demo-controls-column { flex: auto 0 0; margin-right: 40px; } .demo-title { font-weight: bold; color: #4394e5; margin: 20px 0; font-size: 20px; } .demo-control { display: flex; margin-bottom: 10px; max-width: 200px; flex-wrap: wrap; } .demo-control input { display: block; flex: 100%; } .demo-label { display: flex; margin-bottom: 10px; } .demo-label input { margin-right: 10px; } .demo-control label, .demo-label { font-weight: bold; } .demo-value { margin-left: 6px; } </style> </head> <body > <div class="demo"> <svg class="demo-svg" viewBox="-40 -30 580 230"></svg> <a target="_blank" href="https://muffinman.io/blog/draw-svg-rope-using-javascript/">Read the blog post »</a> <div class="demo-controls"> <div class="demo-controls-column"> <div class="demo-title">Options</div> <label class="demo-label"> <input class="demo-checkbox-animate" type="checkbox" checked /> Animate </label> <div class="demo-control"> <label for="demo-step">Segment width</label> <span class="demo-value"></span> <input data-key="step" type="range" min="3" max="50" step="1" value="20" id="demo-step" /> </div> <div class="demo-control"> <label for="demo-thickness">Rope thickness</label> <span class="demo-value"></span> <input data-key="thickness" type="range" min="3" max="50" step="1" value="40" id="demo-thickness" /> </div> <div class="demo-control"> <label for="demo-angle">Skew angle</label> <span class="demo-value"></span> <input data-key="angle" type="range" min="0" max="90" step="1" value="45" id="demo-angle" /> </div> </div> <div class="demo-controls-column"> <div class="demo-title">Render</div> <label class="demo-label"> <input data-key="path" class="demo-checkbox" type="checkbox" /> Path </label> <label class="demo-label"> <input data-key="points" class="demo-checkbox" type="checkbox" /> Points </label> <label class="demo-label"> <input data-key="normals" class="demo-checkbox" type="checkbox" /> Normals </label> <label class="demo-label"> <input data-key="polygons" class="demo-checkbox" type="checkbox" /> Square-ish segments </label> <label class="demo-label"> <input data-key="polygonsRounded" class="demo-checkbox" type="checkbox" /> Rounded square-ish segments </label> <label class="demo-label"> <input data-key="segments" class="demo-checkbox" type="checkbox" /> Segments </label> <label class="demo-label"> <input data-key="rope" class="demo-checkbox" type="checkbox" checked />Rounded segments (finished rope) </label> </div> <div class="demo-controls-column"> <div class="demo-title">Colors</div> <label class="demo-label"> <input class="demo-radio" type="radio" name="demo-colors" value="transparent" /> Transparent </label> <label class="demo-label"> <input class="demo-radio" type="radio" name="demo-colors" value="white" /> White </label> <label class="demo-label"> <input class="demo-radio" type="radio" checked name="demo-colors" value="natural" /> Natural </label> <label class="demo-label"> <input class="demo-radio" type="radio" name="demo-colors" value="rainbow" /> Rainbow </label> </div> </div> </div> <script> // ----- VECTORS ----- // function multiplyVector(v, scalar) { return { x: v.x * scalar, y: v.y * scalar }; } function getVector(a, b) { return { x: b.x - a.x, y: b.y - a.y }; } function addVectors(a, b) { return { x: a.x + b.x, y: a.y + b.y }; } // ----- MATH ----- // function getPointOnLine(start, end, ratio) { const vector = getVector(start, end); const v = multiplyVector(vector, ratio); return { x: start.x + v.x, y: start.y + v.y }; } function getAngleBetweenThreePoints(a, b, c) { const vectorBA = getVector(a, b); const vectorBC = getVector(c, b); const angle = Math.atan2(vectorBC.y, vectorBC.x) - Math.atan2(vectorBA.y, vectorBA.x); return angle; } // ----- CHAIKIN ----- // function cut(start, end, ratio) { const r1 = { x: start.x * (1 - ratio) + end.x * ratio, y: start.y * (1 - ratio) + end.y * ratio }; const r2 = { x: start.x * ratio + end.x * (1 - ratio), y: start.y * ratio + end.y * (1 - ratio) }; return [r1, r2]; } function chaikin(curve, iterations = 1, closed = false, ratio = 0.25) { if (ratio > 0.5) { ratio = 1 - ratio; } for (let i = 0; i < iterations; i++) { let refined = []; refined.push(curve[0]); for (let j = 1; j < curve.length; j++) { let points = cut(curve[j - 1], curve[j], ratio); refined = refined.concat(points); } if (closed) { refined.shift(); refined = refined.concat(cut(curve[curve.length - 1], curve[0], ratio)); } else { refined.push(curve[curve.length - 1]); } curve = refined; } return curve; } // ----- ROPE ----- // function getPathPoints(d, step = 10) { // For potential NodeJS version // https://www.npmjs.com/package/svg-path-properties const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttribute("d", d); const length = path.getTotalLength(); const count = length / step; const points = []; for (let i = 0; i < count + 1; i++) { const n = i * step; points.push(path.getPointAtLength(n)); } const vectorStart = getVector(points[1], points[0]); const vectorEnd = getVector( points[points.length - 2], points[points.length - 1]); return [ // Add helper points at the start addVectors(points[0], vectorStart), ...points, // and end addVectors(points[points.length - 1], vectorEnd)]; } // Takes three points and returns two points. // Points are located at the end of a vector which is bisector of the angle between these three points. // The distance is "thickness" param /* • outerPoint[0] / / v1 •------• v2 / \ / • v3 outerPoint[1] • */ function getOuterPoints(v1, v2, v3, thickness, angleOffset = 0) { /* v1 •------• v2 angle1 / \ / • v3 */ let angle1 = getAngleBetweenT.........完整代码请登录后点击上方下载按钮下载查看
网友评论0