| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <style> | |
| html { | |
| font-family: Helvetica, Arial, sans-serif; | |
| font-size: 100%; | |
| } | |
| .controls { | |
| margin: 1em 0; | |
| } | |
| button { | |
| display: inline-block; | |
| border-radius: 3px; | |
| border: none; | |
| font-size: 0.9rem; | |
| padding: 0.4rem 0.8em; | |
| background: #69c773; | |
| border-bottom: 1px solid #498b50; | |
| color: white; | |
| -webkit-font-smoothing: antialiased; | |
| font-weight: bold; | |
| margin: 0 0.25rem; | |
| text-align: center; | |
| } | |
| button:hover, button:focus { | |
| opacity: 0.75; | |
| cursor: pointer; | |
| } | |
| button:active { | |
| opacity: 1; | |
| box-shadow: 0 -3px 10px rgba(0, 0, 0, 0.1) inset; | |
| } | |
| </style> | |
| <! set height back to 500 /> | |
| <svg id="svg" width="800" height="500" | |
| xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | |
| <defs> | |
| <radialGradient id="grad1" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> | |
| <stop offset="0%" style="stop-color:rgb(0,0,255); stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:rgb(0,0,255); stop-opacity:0" /> | |
| </radialGradient> | |
| <radialGradient id="grad2" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> | |
| <stop offset="0%" style="stop-color:rgb(0,255,0); stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:rgb(0,255,0); stop-opacity:0" /> | |
| </radialGradient> | |
| <radialGradient id="grad3" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> | |
| <stop offset="0%" style="stop-color:rgb(255,0,0); stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:rgb(255,0,0); stop-opacity:0" /> | |
| </radialGradient> | |
| <radialGradient id="grad4" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> | |
| <stop offset="0%" style="stop-color:rgb(192,63,192); stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:rgb(192,63,192); stop-opacity:0" /> | |
| </radialGradient> | |
| <radialGradient id="grad5" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> | |
| <stop offset="0%" style="stop-color:rgb(127,127,0); stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:rgb(127,127,0); stop-opacity:0" /> | |
| </radialGradient> | |
| <radialGradient id="grad6" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> | |
| <stop offset="0%" style="stop-color:rgb(127,0,127); stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:rgb(127,0,127); stop-opacity:0" /> | |
| </radialGradient> | |
| <radialGradient id="grad7" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> | |
| <stop offset="0%" style="stop-color:rgb(0,127,127); stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:rgb(0,127,127); stop-opacity:0" /> | |
| </radialGradient> | |
| <radialGradient id="grad8" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> | |
| <stop offset="0%" style="stop-color:rgb(63,192,63); stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:rgb(63,192,63); stop-opacity:0" /> | |
| </radialGradient> | |
| </defs> | |
| <path id="circleFill" d="M300,200 A 100,100 0,0,0 300,200" fill="#777" fill-opacity="0" /> | |
| <path id="circle" d="M300,200 A 100,100 0,0,0 300,200" fill="none" stroke="black" /> | |
| <! elements for keyframe 1 /> | |
| <text id="spanWedgeDesc" fill-opacity="0" > | |
| All spans are contained by a wedge. | |
| </text> | |
| <path id="span1" d="M200,200 Q300,300 200,300" fill="none" stroke="black" stroke-opacity="0"/> | |
| <path id="span2" d="M200,200 C100,300 100,400 200,300" fill="none" stroke="black" stroke-opacity="0"/> | |
| <path id="span3" d="M200,200 C300,100 100,400 300,200" fill="none" stroke="black" stroke-opacity="0"/> | |
| <path id="wedge1" d="M200,200 L500,500 A 424.26,424.26 0,0,1 200,624.26 z" fill="url(#grad1)" fill-opacity="0"/> | |
| <path id="wedge2" d="M200,200 L200,624.26 A 424.26,424.26 0,0,1 -100,500 z" fill="url(#grad2)" fill-opacity="0"/> | |
| <path id="wedge3" d="M200,200 L500,-100 A 424.26,424.26 0,0,1 240,622.5 z" fill="url(#grad3)" fill-opacity="0"/> | |
| <! keyframe 2 /> | |
| <text id="trivialWedgeDesc1" fill-opacity="0" > | |
| Wedges that don't overlap can be | |
| </text> | |
| <text id="trivialWedgeDesc2" y="240" fill-opacity="0" > | |
| easily sorted. | |
| </text> | |
| <path id="span4" d="M200,200 Q300,300 400,300" fill="none" stroke="black" stroke-opacity="0"/> | |
| <path id="span5" d="M200,200 Q280,320 200,400" fill="none" stroke="black" stroke-opacity="0"/> | |
| <path id="span6" d="M200,200 Q60,340 100,400" fill="none" stroke="black" stroke-opacity="0"/> | |
| <path id="wedge4" d="M200,200 L500,500 A 424.26,424.26 0,0,1 579.47,389.74 z" fill="url(#grad1)" fill-opacity="0"/> | |
| <path id="wedge5" d="M200,200 L389.74,579.47 A 424.26,424.26 0,0,1 200,500 z" fill="url(#grad2)" fill-opacity="0"/> | |
| <path id="wedge6" d="M200,200 L10.26,579.47 A 424.26,424.26 0,0,1 -100,500 z" fill="url(#grad3)" fill-opacity="0"/> | |
| <! keyframe 3 /> | |
| <text id="sectorDesc1" fill-opacity="0" > | |
| A sector is a wedge of a circle | |
| </text> | |
| <text id="sectorDesc2" y="240" fill-opacity="0" > | |
| containing a range of points. | |
| </text> | |
| <g id="xaxis" stroke-opacity="0" fill-opacity="0"> | |
| <path d="M100,200 L300,200" fill="none" stroke="rgb(191,191,191)"/> | |
| <text x="100" y="220" fill="rgb(191,191,191)">-X</text> | |
| <text x="300" y="220" text-anchor="end" fill="rgb(191,191,191)">+X</text> | |
| </g> | |
| <g id="yaxis" stroke-opacity="0" fill-opacity="0"> | |
| <path d="M200,100 L200,300" fill="none" stroke="rgb(191,191,191)"/> | |
| <text x="205" y="100" alignment-baseline="hanging" fill="rgb(191,191,191)">-Y</text> | |
| <text x="205" y="300" fill="rgb(191,191,191)">+Y</text> | |
| </g> | |
| <text id="sectorDescXYA" x="500" y="310" fill="rgb(0,0,255)" fill-opacity="0"> | |
| X > 0> Y > 0 Y < X</text> | |
| <text id="sectorDescXYB" x="500" y="360" fill="rgb(0,127,0)" fill-opacity="0"> | |
| X < 0 Y > 0 -Y < X</text> | |
| <text id="sectorDescXYC" x="500" y="410" fill="rgb(255,0,0)" fill-opacity="0"> | |
| X < 0 Y < 0 Y < X</text> | |
| <path id="wedgeXY8" d="M200,200 L500,500 A 424.26,424.26 0,0,1 624.26,200 z" fill="url(#grad1)" fill-opacity="0"/> | |
| <path id="wedgeXY6" d="M200,200 L-100,500 A 424.26,424.26 0,0,1 200,624.26 z" fill="url(#grad2)" fill-opacity="0"/> | |
| <path id="wedgeXY3" d="M200,200 L-100,-100 A 424.26,424.26 0,0,1 200,-175.74 z" fill="url(#grad3)" fill-opacity="0"/> | |
| <! keyframe 4 /> | |
| <text id="lineSingleDesc" fill-opacity="0" > | |
| Line spans are contained by a single sector. | |
| </text> | |
| <text id="sectorDescXY1" x="500" y="460" fill="rgb(192,63,192)" fill-opacity="0"> | |
| X > 0 Y < 0 -Y < X</text> | |
| <text id="sectorDescXY2" x="500" y="460" fill="rgb(127,127,0)" fill-opacity="0"> | |
| X > 0 Y < 0 -Y > X</text> | |
| <text id="sectorDescXY3" x="500" y="460" fill="rgb(255,0,0)" fill-opacity="0"> | |
| X < 0 Y < 0 Y < X</text> | |
| <text id="sectorDescXY4" x="500" y="460" fill="rgb(127,0,127)" fill-opacity="0"> | |
| X < 0 Y < 0 Y > X</text> | |
| <text id="sectorDescXY5" x="500" y="460" fill="rgb(0,127,127)" fill-opacity="0"> | |
| X < 0 Y > 0 -Y < X</text> | |
| <text id="sectorDescXY6" x="500" y="460" fill="rgb(0,127,0)" fill-opacity="0"> | |
| X < 0 Y > 0 -Y < X</text> | |
| <text id="sectorDescXY7" x="500" y="460" fill="rgb(63,192,63)" fill-opacity="0"> | |
| X > 0 Y > 0 Y > X</text> | |
| <text id="sectorDescXY8" x="500" y="460" fill="rgb(0,0,255)" fill-opacity="0"> | |
| X > 0 Y > 0 Y < X</text> | |
| <path id="wedgeXY1" d="M200,200 L500,-100 A 424.26,424.26 0,0,1 624.26,200 z" fill="url(#grad4)" fill-opacity="0"/> | |
| <path id="wedgeXY2" d="M200,200 L200,-175.74 A 424.26,424.26 0,0,1 500,-100 z" fill="url(#grad5)" fill-opacity="0"/> | |
| <path id="wedgeXY4" d="M200,200 L-175.74,200 A 424.26,424.26 0,0,1 -100,-100 z" fill="url(#grad6)" fill-opacity="0"/> | |
| <path id="wedgeXY5" d="M200,200 L-100,500 A 424.26,424.26 0,0,1 -175.74,200 z" fill="url(#grad7)" fill-opacity="0"/> | |
| <path id="wedgeXY7" d="M200,200 L200,624.26 A 424.26,424.26 0,0,1 500,500 z" fill="url(#grad8)" fill-opacity="0"/> | |
| <path id="lineSegment" d="M200,200 L200,624.26" fill="none" stroke="black" stroke-opacity="0"/> | |
| <! keyframe 5 /> | |
| <text id="curveMultipleDesc1" fill-opacity="0" > | |
| A curve span may cover more | |
| </text> | |
| <text id="curveMultipleDesc2" y="240" fill-opacity="0" > | |
| than one sector. | |
| </text> | |
| <path id="curveSegment" d="M200,200 C250,200 300,150 300,100" fill="none" stroke="black" stroke-opacity="0"/> | |
| <path id="curveSegment1" d="M200,200 C250,200 300,150 300,100" fill="none"/> | |
| <path id="curveSegment2" d="M200,200 C250,200 300,150 200,100" fill="none"/> | |
| <path id="curveSegment3" d="M200,200 C350,200 250,-150 170,300" fill="none"/> | |
| <! keyframe 6 /> | |
| <text id="line1DDest1" fill-opacity="0" > | |
| Some lines occupy one-dimensional | |
| </text> | |
| <text id="line1DDest2" y="240" fill-opacity="0" > | |
| sectors. | |
| </text> | |
| <text id="sectorDescXY9" x="500" y="460" fill="rgb(192,92,31)" fill-opacity="0"> | |
| X > 0 Y == 0</text> | |
| <text id="sectorDescXY10" x="500" y="460" fill="rgb(31,92,192)" fill-opacity="0"> | |
| Y > 0 0 == X</text> | |
| <text id="sectorDescXY11" x="500" y="460" fill="rgb(127,63,127)" fill-opacity="0"> | |
| X < 0 Y == X</text> | |
| <path id="horzSegment" d="M200,200 L341.4,200" fill="none" stroke="rgb(192,92,31)" stroke-width="2" stroke-opacity="0"/> | |
| <path id="vertSegment" d="M200,200 L200,341.4" fill="none" stroke="rgb(31,92,192)" stroke-width="2" stroke-opacity="0"/> | |
| <path id="diagSegment" d="M200,200 L100,100" fill="none" stroke="rgb(127,63,127)" stroke-width="2" stroke-opacity="0"/> | |
| <! keyframe 7 /> | |
| <text id="curve1dDesc1" fill-opacity="0" > | |
| Some curves initially occupy | |
| </text> | |
| <text id="curve1dDesc2" y="240" fill-opacity="0" > | |
| one-dimensional sectors, then diverge. | |
| </text> | |
| <path id="cubicSegment" fill="none" stroke="black" /> | |
| <path id="cubicSegment1" d="M200,200 C200,200 200,200 200,200" fill="none" /> | |
| <path id="cubicSegment2" d="M200,200 C250,200 300,200 300,100" fill="none"/> | |
| <text id="sectorNumberDesc" fill-opacity="0" > | |
| Each sector is assigned a number. | |
| </text> | |
| <text id="spanSectorDesc" fill-opacity="0" > | |
| Each span has a bit set for one or more sectors. | |
| </text> | |
| <text id="bitOverDesc" fill-opacity="0" > | |
| Span sets allow rough sorting without angle computation. | |
| </text> | |
| </svg> | |
| <! canvas support /> | |
| <script> | |
| var keyFrameQueue = []; | |
| var animationsPending = []; | |
| var animationsActive = []; | |
| var displayList = []; | |
| var visibleFinished = []; | |
| var animationState = {}; | |
| animationState.reset = function () { | |
| this.start = null; | |
| this.time = 0; | |
| this.requestID = null; | |
| this.paused = false; | |
| this.displayEngine = 'Canvas'; | |
| } | |
| circle.center = { x: 200, y: 200 } | |
| circle.radius = 100; | |
| function assert(condition) { | |
| if (!condition) debugger; | |
| } | |
| function CanvasGrads(ctx) { | |
| var grad1 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); | |
| grad1.addColorStop(0, "rgba(0,0,255, 0.3)"); | |
| grad1.addColorStop(1, "rgba(0,0,255, 0)"); | |
| var grad2 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); | |
| grad2.addColorStop(0, "rgba(0,255,0, 0.3)"); | |
| grad2.addColorStop(1, "rgba(0,255,0, 0)"); | |
| var grad3 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); | |
| grad3.addColorStop(0, "rgba(255,0,0, 0.3)"); | |
| grad3.addColorStop(1, "rgba(255,0,0, 0)"); | |
| var grad4 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); | |
| grad4.addColorStop(0, "rgba(192,63,192, 0.3)"); | |
| grad4.addColorStop(1, "rgba(192,63,192, 0)"); | |
| var grad5 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); | |
| grad5.addColorStop(0, "rgba(127,127,0, 0.3)"); | |
| grad5.addColorStop(1, "rgba(127,127,0, 0)"); | |
| var grad6 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); | |
| grad6.addColorStop(0, "rgba(127,0,127, 0.3)"); | |
| grad6.addColorStop(1, "rgba(127,0,127, 0)"); | |
| var grad7 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); | |
| grad7.addColorStop(0, "rgba(0,127,127, 0.3)"); | |
| grad7.addColorStop(1, "rgba(0,127,127, 0)"); | |
| var grad8 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); | |
| grad8.addColorStop(0, "rgba(63,192,63, 0.3)"); | |
| grad8.addColorStop(1, "rgba(63,192,63, 0)"); | |
| var data = { | |
| grad1: grad1, | |
| grad2: grad2, | |
| grad3: grad3, | |
| grad4: grad4, | |
| grad5: grad5, | |
| grad6: grad6, | |
| grad7: grad7, | |
| grad8: grad8, | |
| }; | |
| return data; | |
| } | |
| function skip_sep(data) { | |
| if (!data.length) { | |
| return data; | |
| } | |
| while (data[0] == ' ' || data[0] == ',') { | |
| data = data.substring(1); | |
| } | |
| return data; | |
| } | |
| function find_points(str, value, count, isRelative, relative) { | |
| var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; | |
| var match; | |
| for (var index = 0; index < count; ++index) { | |
| str = skip_sep(str); | |
| match = numRegEx.exec(str); | |
| assert(match); | |
| var x = Number(match[0]); | |
| str = skip_sep(str); | |
| match = numRegEx.exec(str); | |
| assert(match); | |
| var y = Number(match[0]); | |
| value[index] = { x: x, y : y }; | |
| } | |
| if (isRelative) { | |
| for (var index = 0; index < count; index++) { | |
| value[index].x += relative.x; | |
| value[index].y += relative.y; | |
| } | |
| } | |
| return str.substring(match.index + match[0].length); | |
| } | |
| function find_scalar(str, obj, isRelative, relative) { | |
| var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; | |
| str = skip_sep(str); | |
| var match = numRegEx.exec(str); | |
| obj.value = Number(match[0]); | |
| if (isRelative) { | |
| obj.value += relative; | |
| } | |
| return str.substring(match.index + match[0].length); | |
| } | |
| function parse_path(data) { | |
| var path = "ctx.beginPath();\n"; | |
| var f = {x:0, y:0}; | |
| var c = {x:0, y:0}; | |
| var lastc = {x:0, y:0}; | |
| var points = []; | |
| var op = '\0'; | |
| var previousOp = '\0'; | |
| var relative = false; | |
| for (;;) { | |
| data = skip_sep(data); | |
| if (!data.length) { | |
| break; | |
| } | |
| var ch = data[0]; | |
| if (('0' <= ch && ch <= '9') || ch == '-' || ch == '+') { | |
| assert(op != '\0'); | |
| } else if (ch == ' ' || ch == ',') { | |
| data = skip_sep(data); | |
| } else { | |
| op = ch; | |
| relative = false; | |
| if ('a' <= op && op <= 'z') { | |
| op = op.toUpperCase(); | |
| relative = true; | |
| } | |
| data = data.substring(1); | |
| data = skip_sep(data); | |
| } | |
| switch (op) { | |
| case 'A': | |
| var radii = []; | |
| data = find_points(data, radii, 1, false, null); | |
| var xaxisObj = {}; | |
| data = find_scalar(data, xaxisObj, false, null); | |
| var largeArcObj = {}; | |
| data = find_scalar(data, largeArcObj, false, null); | |
| var sweepObj = {}; | |
| data = find_scalar(data, sweepObj, false, null); | |
| data = find_points(data, points, 1, relative, c); | |
| var mid = { x: (c.x + points[0].x) / 2, y: (c.y + points[0].y) / 2 }; | |
| var midVec = { x: mid.x - c.x, y: mid.y - c.y }; | |
| var midLenSqr = midVec.x * midVec.x + midVec.y * midVec.y; | |
| var radius = radii[0].x; | |
| var scale = Math.sqrt(midLenSqr) / Math.sqrt(radius * radius - midLenSqr); | |
| var tangentPt = { x: mid.x + midVec.y * scale, | |
| y: mid.y - midVec.x * scale }; | |
| path += "ctx.arcTo(" + tangentPt.x + "," + tangentPt.y + "," | |
| + points[0].x + "," + points[0].y + "," + radius + ");\n"; | |
| c = points[0]; | |
| break; | |
| case 'M': | |
| data = find_points(data, points, 1, relative, c); | |
| path += "ctx.moveTo(" + points[0].x + "," + points[0].y + ");\n"; | |
| op = 'L'; | |
| c = points[0]; | |
| break; | |
| case 'L': | |
| data = find_points(data, points, 1, relative, c); | |
| path += "ctx.lineTo(" + points[0].x + "," + points[0].y + ");\n"; | |
| c = points[0]; | |
| break; | |
| case 'H': { | |
| var xObj = {}; | |
| data = find_scalar(data, xObj, relative, c.x); | |
| path += "ctx.lineTo(" + xObj.value + "," + c.y + ");\n"; | |
| c.x = xObj.value; | |
| } break; | |
| case 'V': { | |
| var yObj = {}; | |
| data = find_scalar(data, y, relative, c.y); | |
| path += "ctx.lineTo(" + c.x + "," + yObj.value+ ");\n"; | |
| c.y = yObj.value; | |
| } break; | |
| case 'C': | |
| data = find_points(data, points, 3, relative, c); | |
| path += "ctx.bezierCurveTo(" + points[0].x + "," + points[0].y + "," | |
| + points[1].x + "," + points[1].y + "," | |
| + points[2].x + "," + points[2].y + ");\n"; | |
| lastc = points[1]; | |
| c = points[2]; | |
| break; | |
| case 'S': | |
| var pts2_3 = []; | |
| data = find_points(data, pts2_3, 2, relative, c); | |
| points[0] = c; | |
| points[1] = pts2_3[0]; | |
| points[2] = pts2_3[1]; | |
| if (previousOp == 'C' || previousOp == 'S') { | |
| points[0].x -= lastc.x - c.x; | |
| points[0].y -= lastc.y - c.y; | |
| } | |
| path += "ctx.bezierCurveTo(" + points[0].x + "," + points[0].y + "," | |
| + points[1].x + "," + points[1].y + "," | |
| + points[2].x + "," + points[2].y + ");\n"; | |
| lastc = points[1]; | |
| c = points[2]; | |
| break; | |
| case 'Q': // Quadratic Bezier Curve | |
| data = find_points(data, points, 2, relative, c); | |
| path += "ctx.quadraticCurveTo(" + points[0].x + "," + points[0].y + "," | |
| + points[1].x + "," + points[1].y + ");\n"; | |
| lastc = points[0]; | |
| c = points[1]; | |
| break; | |
| case 'T': | |
| var pts2 = []; | |
| data = find_points(data, pts2, 1, relative, c); | |
| points[0] = pts2[0]; | |
| points[1] = pts2[0]; | |
| if (previousOp == 'Q' || previousOp == 'T') { | |
| points[0].x = c.x * 2 - lastc.x; | |
| points[0].y = c.y * 2 - lastc.y; | |
| } | |
| path += "ctx.quadraticCurveTo(" + points[0].x + "," + points[0].y + "," | |
| + points[1].x + "," + points[1].y + ");\n"; | |
| path.quadTo(points[0], points[1]); | |
| lastc = points[0]; | |
| c = points[1]; | |
| break; | |
| case 'Z': | |
| path += "ctx.closePath();\n"; | |
| c = f; | |
| op = '\0'; | |
| break; | |
| case '~': | |
| var args = []; | |
| data = find_points(data, args, 2, false, null); | |
| path += "moveTo(" + args[0].x + "," + args[0].y + ");\n"; | |
| path += "lineTo(" + args[1].x + "," + args[1].y + ");\n"; | |
| break; | |
| default: | |
| return false; | |
| } | |
| if (previousOp == 0) { | |
| f = c; | |
| } | |
| previousOp = op; | |
| } | |
| return path; | |
| } | |
| function CanvasPaths(ctx) { | |
| var svgStrs = { | |
| // keyframe 1 | |
| span1: "M200,200 Q300,300 200,300", | |
| span2: "M200,200 C100,300 100,400 200,300", | |
| span3: "M200,200 C300,100 100,400 300,200", | |
| wedge1: "M200,200 L500,500 A 424.26,424.26 0,0,1 200,624.26 z", | |
| wedge2: "M200,200 L200,624.26 A 424.26,424.26 0,0,1 -100,500 z", | |
| wedge3: "M200,200 L500,-100 A 424.26,424.26 0,0,1 240,622.5 z", | |
| // keyframe 2 | |
| span4: "M200,200 Q300,300 400,300", | |
| span5: "M200,200 Q280,320 200,400", | |
| span6: "M200,200 Q60,340 100,400", | |
| wedge4: "M200,200 L500,500 A 424.26,424.26 0,0,1 579.47,389.74 z", | |
| wedge5: "M200,200 L389.74,579.47 A 424.26,424.26 0,0,1 200,500 z", | |
| wedge6: "M200,200 L10.26,579.47 A 424.26,424.26 0,0,1 -100,500 z", | |
| // keyframe 3 | |
| xaxis: "M100,200 L300,200", | |
| yaxis: "M200,100 L200,300", | |
| wedgeXY8: "M200,200 L500,500 A 424.26,424.26 0,0,1 624.26,200 z", | |
| wedgeXY6: "M200,200 L-100,500 A 424.26,424.26 0,0,1 200,624.26 z", | |
| wedgeXY3: "M200,200 L-100,-100 A 424.26,424.26 0,0,1 200,-175.74 z", | |
| // keyframe 4 | |
| wedgeXY1: "M200,200 L500,-100 A 424.26,424.26 0,0,1 624.26,200 z", | |
| wedgeXY2: "M200,200 L200,-175.74 A 424.26,424.26 0,0,1 500,-100 z", | |
| wedgeXY4: "M200,200 L-175.74,200 A 424.26,424.26 0,0,1 -100,-100 z", | |
| wedgeXY5: "M200,200 L-100,500 A 424.26,424.26 0,0,1 -175.74,200 z", | |
| wedgeXY7: "M200,200 L200,624.26 A 424.26,424.26 0,0,1 500,500 z", | |
| lineSegment: "M200,200 L200,624.26", | |
| // keyframe 5 | |
| curveSegment: "M200,200 C250,200 300,150 300,100", | |
| curveSegment1: "M200,200 C250,200 300,150 300,100", | |
| curveSegment2: "M200,200 C250,200 300,150 200,100", | |
| curveSegment3: "M200,200 C350,200 250,-150 170,300", | |
| // keyframe 6 | |
| horzSegment: "M200,200 L341.4,200", | |
| vertSegment: "M200,200 L200,341.4", | |
| diagSegment: "M200,200 L100,100", | |
| // keyframe 7 | |
| cubicSegment: "M200,200 C200,200 200,200 200,200", | |
| cubicSegment1: "M200,200 C200,200 200,200 200,200", | |
| cubicSegment2: "M200,200 C250,200 300,200 300,100", | |
| }; | |
| var paths = []; | |
| var keys = Object.keys(svgStrs); | |
| for (var index in keys) { | |
| var key = keys[index]; | |
| var str = svgStrs[key]; | |
| var path = parse_path(str); | |
| var record = []; | |
| paths[key] = { | |
| str: str, | |
| funcBody: path, | |
| }; | |
| } | |
| return paths; | |
| } | |
| function canvas_fill_font(record) { | |
| assert(record); | |
| var str = 'ctx.font = "normal 1.3rem Helvetica,Arial";\n'; | |
| if (record.fillStyle) { | |
| str += 'ctx.fillStyle = ' + record.fillStyle + ';\n'; | |
| } | |
| return str; | |
| } | |
| function canvas_fill_text(record) { | |
| assert(record); | |
| assert(typeof record.fillText == 'string'); | |
| return 'ctx.fillText("' + record.fillText + '"'; | |
| } | |
| function canvas_xy(record) { | |
| var x = typeof record.x == "number" ? record.x : 400; | |
| var y = typeof record.y == "number" ? record.y : 200; | |
| return ', ' + x + ', ' + y + ');\n'; | |
| } | |
| function canvas_text_xy(record) { | |
| return canvas_fill_text(record) + canvas_xy(record); | |
| } | |
| function add_canvas_stroke(paths, data, id, strokeStyle) { | |
| var record = {}; | |
| record.data = paths[id].funcBody; | |
| record.style = 'ctx.strokeStyle = ' + (strokeStyle ? strokeStyle : '"black"') + ';\n'; | |
| record.draw = 'ctx.stroke();\n'; | |
| record.func = new Function('ctx', record.data + record.style + record.draw); | |
| return data[id] = record; | |
| } | |
| function add_canvas_style(record, style) { | |
| record.style += style; | |
| record.func = new Function('ctx', record.data + record.style + record.draw); | |
| } | |
| function add_canvas_fill(paths, data, id, fillStyle) { | |
| var record = {}; | |
| record.data = paths[id].funcBody; | |
| record.style = 'ctx.fillStyle = ' + (fillStyle ? fillStyle : '"black"') + ';\n'; | |
| record.draw = 'ctx.fill();\n'; | |
| record.func = new Function('ctx', record.data + record.style + record.draw); | |
| return data[id] = record; | |
| } | |
| function add_canvas_text(data, id, params) { | |
| var record = {}; | |
| record.style = canvas_fill_font(params); | |
| record.draw = canvas_fill_text(params); | |
| record.position = canvas_xy(params); | |
| record.x = params.x; | |
| record.y = params.y; | |
| record.func = new Function('ctx', record.style + record.draw + record.position); | |
| return data[id] = record; | |
| } | |
| function keyframe1(grads, paths) { | |
| var data = []; | |
| add_canvas_text(data, "spanWedgeDesc", { fillText:"All spans are contained by a wedge" } ); | |
| add_canvas_stroke(paths, data, "span1"); | |
| add_canvas_stroke(paths, data, "span2"); | |
| add_canvas_stroke(paths, data, "span3"); | |
| add_canvas_fill(paths, data, "wedge1", "grads.grad1"); | |
| add_canvas_fill(paths, data, "wedge2", "grads.grad2"); | |
| add_canvas_fill(paths, data, "wedge3", "grads.grad3"); | |
| return data; | |
| } | |
| function keyframe2(grads, paths) { | |
| var data = []; | |
| add_canvas_text(data, "trivialWedgeDesc1", { fillText:"Wedges that don't overlap can be" } ); | |
| add_canvas_text(data, "trivialWedgeDesc2", { fillText:"easily sorted.", y:240 } ); | |
| add_canvas_stroke(paths, data, "span4").debug = true; | |
| add_canvas_stroke(paths, data, "span5"); | |
| add_canvas_stroke(paths, data, "span6"); | |
| add_canvas_fill(paths, data, "wedge4", "grads.grad1"); | |
| add_canvas_fill(paths, data, "wedge5", "grads.grad2"); | |
| add_canvas_fill(paths, data, "wedge6", "grads.grad3"); | |
| return data; | |
| } | |
| function setup_axes(paths, data) { | |
| var color = '"rgb(191,191,191)"'; | |
| var xaxis = add_canvas_stroke(paths, data, "xaxis", color); | |
| xaxis.funcBody = canvas_fill_font( { fillStyle:color } ); | |
| xaxis.funcBody += canvas_text_xy( { fillText:"-X", x:100, y:220 } ); | |
| xaxis.funcBody += "ctx.textAlign = 'right';\n"; | |
| xaxis.funcBody += canvas_text_xy( { fillText:"+X", x:300, y:220 } ); | |
| xaxis.func = new Function('ctx', xaxis.data + xaxis.style + xaxis.draw + xaxis.funcBody); | |
| var yaxis = add_canvas_stroke(paths, data, "yaxis", color); | |
| yaxis.funcBody = canvas_fill_font( { fillStyle:color } ); | |
| yaxis.funcBody += "ctx.textBaseline = 'hanging';\n"; | |
| yaxis.funcBody += canvas_text_xy( { fillText:"-Y", x:205, y:100 } ); | |
| yaxis.funcBody += "ctx.textBaseline = 'alphabetic';\n"; | |
| yaxis.funcBody += canvas_text_xy( { fillText:"+Y", x:205, y:300 } ); | |
| yaxis.func = new Function('ctx', yaxis.data + yaxis.style + yaxis.draw + yaxis.funcBody); | |
| } | |
| function keyframe3(grads, paths) { | |
| var data = []; | |
| add_canvas_text(data, "sectorDesc1", { fillText:"A sector is a wedge of a circle" } ); | |
| add_canvas_text(data, "sectorDesc2", { fillText:"containing a range of points.", y:240 } ); | |
| setup_axes(paths, data); | |
| add_canvas_text(data, "sectorDescXYA", | |
| { fillText:"X > 0 Y > 0 Y < X", x:500, y:310, fillStyle:'"rgb(0,0,255)"'} ); | |
| add_canvas_text(data, "sectorDescXYB", | |
| { fillText:"X < 0 Y > 0 -Y < X", x:500, y:360, fillStyle:'"rgb(0,127,0)"'} ); | |
| add_canvas_text(data, "sectorDescXYC", | |
| { fillText:"X < 0 Y < 0 Y < X", x:500, y:410, fillStyle:'"rgb(255,0,0)"'} ); | |
| add_canvas_fill(paths, data, "wedgeXY8", "grads.grad1"); | |
| add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); | |
| add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); | |
| return data; | |
| } | |
| function keyframe4(grads, paths) { | |
| var data = []; | |
| setup_axes(paths, data); | |
| add_canvas_text(data, "lineSingleDesc", | |
| { fillText:"Line spans are contained by a single sector." } ); | |
| add_canvas_text(data, "sectorDescXY1", | |
| { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); | |
| add_canvas_text(data, "sectorDescXY2", | |
| { fillText:"X > 0 Y < 0 -Y > X", x:500, y:460, fillStyle:'"rgb(127,127,0)"'} ); | |
| add_canvas_text(data, "sectorDescXY3", | |
| { fillText:"X < 0 Y < 0 Y < X", x:500, y:460, fillStyle:'"rgb(255,0,0)"'} ); | |
| add_canvas_text(data, "sectorDescXY4", | |
| { fillText:"X < 0 Y < 0 Y > X", x:500, y:460, fillStyle:'"rgb(127,0,127)"'} ); | |
| add_canvas_text(data, "sectorDescXY5", | |
| { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,127)"'} ); | |
| add_canvas_text(data, "sectorDescXY6", | |
| { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,0)"'} ); | |
| add_canvas_text(data, "sectorDescXY7", | |
| { fillText:"X > 0 Y > 0 Y > X", x:500, y:460, fillStyle:'"rgb(63,192,63)"'} ); | |
| add_canvas_text(data, "sectorDescXY8", | |
| { fillText:"X > 0 Y > 0 Y < X", x:500, y:460, fillStyle:'"rgb(0,0,255)"'} ); | |
| add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); | |
| add_canvas_fill(paths, data, "wedgeXY2", "grads.grad5"); | |
| add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); | |
| add_canvas_fill(paths, data, "wedgeXY4", "grads.grad6"); | |
| add_canvas_fill(paths, data, "wedgeXY5", "grads.grad7"); | |
| add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); | |
| add_canvas_fill(paths, data, "wedgeXY7", "grads.grad8"); | |
| add_canvas_fill(paths, data, "wedgeXY8", "grads.grad1"); | |
| add_canvas_stroke(paths, data, "lineSegment"); | |
| return data; | |
| } | |
| function keyframe5(grads, paths) { | |
| var data = []; | |
| setup_axes(paths, data); | |
| add_canvas_text(data, "curveMultipleDesc1", | |
| { fillText:"A curve span may cover more" } ); | |
| add_canvas_text(data, "curveMultipleDesc2", | |
| { fillText:"than one sector.", y:240 } ); | |
| add_canvas_stroke(paths, data, "curveSegment"); | |
| add_canvas_text(data, "sectorDescXY1", | |
| { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); | |
| add_canvas_text(data, "sectorDescXY2", | |
| { fillText:"X > 0 Y < 0 -Y > X", x:500, y:460, fillStyle:'"rgb(127,127,0)"'} ); | |
| add_canvas_text(data, "sectorDescXY3", | |
| { fillText:"X < 0 Y < 0 Y < X", x:500, y:460, fillStyle:'"rgb(255,0,0)"'} ); | |
| add_canvas_text(data, "sectorDescXY4", | |
| { fillText:"X < 0 Y < 0 Y > X", x:500, y:460, fillStyle:'"rgb(127,0,127)"'} ); | |
| add_canvas_text(data, "sectorDescXY5", | |
| { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,127)"'} ); | |
| add_canvas_text(data, "sectorDescXY6", | |
| { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,0)"'} ); | |
| add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); | |
| add_canvas_fill(paths, data, "wedgeXY2", "grads.grad5"); | |
| add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); | |
| add_canvas_fill(paths, data, "wedgeXY4", "grads.grad6"); | |
| add_canvas_fill(paths, data, "wedgeXY5", "grads.grad7"); | |
| add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); | |
| return data; | |
| } | |
| function keyframe6(grads, paths) { | |
| var data = []; | |
| setup_axes(paths, data); | |
| add_canvas_text(data, "line1DDest1", | |
| { fillText:"Some lines occupy one-dimensional" } ); | |
| add_canvas_text(data, "line1DDest2", | |
| { fillText:"sectors.", y:240 } ); | |
| add_canvas_text(data, "sectorDescXY9", | |
| { fillText:"X > 0 Y == 0", x:500, y:460, fillStyle:'"rgb(192,92,31)"' } ); | |
| add_canvas_text(data, "sectorDescXY10", | |
| { fillText:"Y > 0 0 == X", x:500, y:460, fillStyle:'"rgb(31,92,192)"' } ); | |
| add_canvas_text(data, "sectorDescXY11", | |
| { fillText:"X < 0 Y == X", x:500, y:460, fillStyle:'"rgb(127,63,127)"' } ); | |
| var horz = add_canvas_stroke(paths, data, "horzSegment", '"rgb(192,92,31)"'); | |
| add_canvas_style(horz, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); | |
| var vert = add_canvas_stroke(paths, data, "vertSegment", '"rgb(31,92,192)"'); | |
| add_canvas_style(vert, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); | |
| var diag = add_canvas_stroke(paths, data, "diagSegment", '"rgb(127,63,127)"'); | |
| add_canvas_style(diag, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); | |
| return data; | |
| } | |
| function keyframe7(grads, paths) { | |
| var data = []; | |
| setup_axes(paths, data); | |
| add_canvas_text(data, "curve1dDesc1", | |
| { fillText:"Some curves initially occupy" } ); | |
| add_canvas_text(data, "curve1dDesc2", | |
| { fillText:"one-dimensional sectors, then diverge.", y:240 } ); | |
| add_canvas_stroke(paths, data, "cubicSegment"); | |
| add_canvas_text(data, "sectorDescXY1", | |
| { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); | |
| add_canvas_text(data, "sectorDescXY9", | |
| { fillText:"X > 0 Y == 0", x:500, y:460, fillStyle:'"rgb(192,92,31)"' } ); | |
| var horz = add_canvas_stroke(paths, data, "horzSegment", '"rgb(192,92,31)"'); | |
| add_canvas_style(horz, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); | |
| add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); | |
| return data; | |
| } | |
| var canvasData = null; | |
| function CanvasInit(keyframe) { | |
| canvasData = window[keyframe](grads, paths); | |
| } | |
| </script> | |
| <script> | |
| function interp(A, B, t) { | |
| return A + (B - A) * t; | |
| } | |
| function interp_cubic_coords(x1, x2, x3, x4, t) | |
| { | |
| var ab = interp(x1, x2, t); | |
| var bc = interp(x2, x3, t); | |
| var cd = interp(x3, x4, t); | |
| var abc = interp(ab, bc, t); | |
| var bcd = interp(bc, cd, t); | |
| var abcd = interp(abc, bcd, t); | |
| return abcd; | |
| } | |
| function cubic_partial(value, p) { | |
| var x1 = p[0], y1 = p[1], x2 = p[2], y2 = p[3]; | |
| var x3 = p[4], y3 = p[5], x4 = p[6], y4 = p[7]; | |
| var t1 = 0, t2 = value; | |
| var ax = interp_cubic_coords(x1, x2, x3, x4, t1); | |
| var ay = interp_cubic_coords(y1, y2, y3, y4, t1); | |
| var ex = interp_cubic_coords(x1, x2, x3, x4, (t1*2+t2)/3); | |
| var ey = interp_cubic_coords(y1, y2, y3, y4, (t1*2+t2)/3); | |
| var fx = interp_cubic_coords(x1, x2, x3, x4, (t1+t2*2)/3); | |
| var fy = interp_cubic_coords(y1, y2, y3, y4, (t1+t2*2)/3); | |
| var dx = interp_cubic_coords(x1, x2, x3, x4, t2); | |
| var dy = interp_cubic_coords(y1, y2, y3, y4, t2); | |
| var mx = ex * 27 - ax * 8 - dx; | |
| var my = ey * 27 - ay * 8 - dy; | |
| var nx = fx * 27 - ax - dx * 8; | |
| var ny = fy * 27 - ay - dy * 8; | |
| var bx = (mx * 2 - nx) / 18; | |
| var by = (my * 2 - ny) / 18; | |
| var cx = (nx * 2 - mx) / 18; | |
| var cy = (ny * 2 - my) / 18; | |
| var array = [ | |
| ax, ay, bx, by, cx, cy, dx, dy | |
| ]; | |
| return array; | |
| } | |
| function evaluate_at(value, p) { | |
| var array = []; | |
| for (var index = 0; index < p.length; ++index) { | |
| var func = new Function('value', 'return ' + p[index] + ';'); | |
| array[index] = func(value); | |
| } | |
| return array; | |
| } | |
| function interpolate_at(value, p) { | |
| var array = []; | |
| var start = p[0]; | |
| var end = p[1]; | |
| assert(typeof end == typeof start); | |
| switch (typeof start) { | |
| case 'object': | |
| for (var index = 0; index < start.length; ++index) { | |
| array[index] = interp(start[index], end[index], value); | |
| } | |
| break; | |
| case 'number': | |
| array[index] = interp(start, end, value); | |
| break; | |
| default: | |
| debugger; | |
| } | |
| return array; | |
| } | |
| function AnimationAddCommon(timing, range, attr, inParams) { | |
| var animation = { | |
| timing: timing, | |
| range: range, | |
| attr: attr, | |
| inParams: inParams, | |
| duration: timing[1] - timing[0], | |
| remaining: timing[1] - timing[0], | |
| firstStep: true, | |
| } | |
| animationsPending.push(animation); | |
| return animation; | |
| } | |
| function AnimationAddSVG(timing, element, range, attr, inParams) { | |
| var animation = AnimationAddCommon(timing, range, attr, inParams); | |
| animation.element = element; | |
| return animation; | |
| } | |
| function AnimationAddCanvas(timing, element, range, attr, inParams) { | |
| var animation = AnimationAddCommon(timing, range, attr, inParams); | |
| animation.element = canvasData[element]; | |
| assert(animation.element); | |
| animation.firstElement = null; | |
| return animation; | |
| } | |
| function AnimationAdd(timing, e, range, attr, funct, inParams) { | |
| if (!range) { | |
| range = [0, 1]; | |
| } | |
| if (!attr) { | |
| attr = 'opacity'; | |
| } | |
| if (!funct) { | |
| funct = interpolate_at; | |
| } | |
| var element; | |
| switch (animationState.displayEngine) { | |
| case 'SVG': | |
| element = typeof e == 'string' ? document.getElementById(e) : e; | |
| break; | |
| case 'Canvas': | |
| element = typeof e == 'string' ? e : e.id; | |
| break; | |
| default: | |
| debugger; | |
| } | |
| assert(element); | |
| switch (attr) { | |
| case 'path': | |
| if (!inParams) { | |
| inParams = PathDataArray(element); | |
| } | |
| break; | |
| case 'opacity': | |
| if (!inParams) { | |
| inParams = [0, 1]; | |
| } | |
| break; | |
| default: | |
| debugger; | |
| } | |
| var funcBody = 'var outParams = ' + funct.name + '(value, inParams);\n'; | |
| switch (animationState.displayEngine) { | |
| case 'SVG': | |
| switch (attr) { | |
| case 'path': | |
| var verbArray = PathVerbArray(element); | |
| funcBody += 'return '; | |
| for (var index = 0; index < inParams.length; ++index) { | |
| funcBody += '"' + verbArray[index] + '"'; | |
| funcBody += 'outParams[' + index + '];\n'; | |
| } | |
| if (verbArray.length > inParams.length) { | |
| funcBody += '"' + verbArray[verbArray.length - 1] + '"'; | |
| } | |
| funcBody += ';\n'; | |
| var animation = AnimationAddSVG(timing, element, range, "d", inParams); | |
| animation.func = new Function('value', 'inParams', funcBody); | |
| break; | |
| case 'opacity': | |
| if (animation.element.getAttribute("stroke-opacity")) { | |
| animation = AnimationAddSVG(timing, element, range, "stroke-opacity", inParams); | |
| } | |
| if (animation.element.getAttribute("fill-opacity")) { | |
| animation = AnimationAddSVG(timing, element, range, "fill-opacity", inParams); | |
| } | |
| break; | |
| default: | |
| debugger; | |
| } | |
| case 'Canvas': | |
| switch (attr) { | |
| case 'path': | |
| var verbArray = PathVerbArray(element); | |
| for (var index = 0; index < inParams.length; ++index) { | |
| funcBody += verbArray[index]; | |
| funcBody += 'outParams[' + index + ']'; | |
| } | |
| if (verbArray.length > inParams.length) { | |
| funcBody += verbArray[verbArray.length - 1]; | |
| } | |
| animation = AnimationAddCanvas(timing, element, range, attr, inParams); | |
| funcBody += animation.element.style + animation.element.draw; | |
| animation.func = new Function('ctx', 'value', 'inParams', funcBody); | |
| break; | |
| case 'opacity': | |
| animation = AnimationAddCanvas(timing, element, range, attr, inParams); | |
| break; | |
| default: | |
| debugger; | |
| } | |
| break; | |
| default: | |
| debugger; | |
| } | |
| return animation; | |
| } | |
| function path_data_common(element, getValues) { | |
| var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; | |
| var data = []; | |
| var match; | |
| var path; | |
| switch (animationState.displayEngine) { | |
| case 'SVG': path = element.getAttribute("d"); break; | |
| case 'Canvas': path = paths[element].funcBody; break; | |
| default: debugger; | |
| } | |
| if (getValues) { | |
| while ((match = numRegEx.exec(path))) { | |
| data.push(Number(match[0])); | |
| } | |
| } else { | |
| var sIndex = 0; | |
| while ((match = numRegEx.exec(path))) { | |
| if (sIndex < match.index) { | |
| data.push(path.substring(sIndex, match.index)); | |
| } | |
| sIndex = match.index + match[0].length; | |
| } | |
| if (sIndex < path.length) { | |
| data.push(path.substring(sIndex, path.length)); | |
| } | |
| } | |
| return data; | |
| } | |
| function PathDataArray(element) { | |
| return path_data_common(element, true); | |
| } | |
| function PathVerbArray(element) { | |
| return path_data_common(element, false); | |
| } | |
| function PathSet(element, funct, value, params) { | |
| var pathVerbs = PathVerbArray(element); | |
| if (funct) { | |
| params = funct(value, params); | |
| } | |
| var setValue = ''; | |
| for (var index = 0; index < params.length; ++index) { | |
| setValue += pathVerbs[index]; | |
| setValue += params[index]; | |
| } | |
| if (pathVerbs.length > params.length) { | |
| setValue += pathVerbs[pathVerbs.length - 1]; | |
| } | |
| switch (animationState.displayEngine) { | |
| case 'SVG': | |
| element.setAttribute('d', setValue); | |
| break; | |
| case 'Canvas': | |
| element.func = new Function('ctx', setValue + element.style + element.draw); | |
| break; | |
| default: | |
| debugger; | |
| } | |
| } | |
| function RemoveFromArray(array, element) { | |
| for (var index in array) { | |
| var record = array[index]; | |
| if (record.element == element) { | |
| array.splice(index, 1); | |
| break; | |
| } | |
| } | |
| } | |
| function EndAnimationCanvas(animation, visibleFinished) { | |
| var changeAlpha = "opacity" == animation.attr; | |
| if (!changeAlpha || animation.range[1] > 0) { | |
| if (changeAlpha) { | |
| ctx.save(); | |
| ctx.globalAlpha = animation.range[1]; | |
| } | |
| if (animation.func) { | |
| animation.func(ctx, animation.range[animation.range.length - 1], animation.inParams); | |
| } else { | |
| animation.element.func(ctx); | |
| } | |
| if (changeAlpha) { | |
| ctx.restore(); | |
| } | |
| // if (visibleFinished) { | |
| // visibleFinished.push(animation); | |
| // } | |
| } else { | |
| // if (visibleFinished) { | |
| // RemoveFromArray(visibleFinished, animation.element); | |
| // } | |
| } | |
| } | |
| /* start here | |
| canvas: | |
| display list : | |
| for each element (canvas) | |
| save | |
| set global alpha (override) | |
| create geometry (override) | |
| create style (override) | |
| draw | |
| restore | |
| maybe each action should have an override slot | |
| animations write to the slot | |
| each element in display list then iterates overrides once the animations complete the frame | |
| so, first -- | |
| active animations update the display list | |
| next -- | |
| active animations install themselves in override slots | |
| finally | |
| display list is iterated, calling override slots | |
| ---------------- | |
| svg: | |
| display list is implicit | |
| active animations write element attributes | |
| */ | |
| function EndAnimationSVG(animation, visibleFinished) { | |
| switch (animation.attr) { | |
| case "opacity": | |
| animation.element.setAttribute(animation.attribute, animation.range[1]); | |
| if (animation.range[1] > 0) { | |
| visibleFinished.push(animation); | |
| } else { | |
| RemoveFromArray(visibleFinished, animation.element); | |
| } | |
| break; | |
| case "path": | |
| var attrStr = animation.func(animation.range[1], animation.inParams); | |
| animation.element.setAttribute(animation.attribute, attrStr); | |
| break; | |
| default: | |
| debugger; | |
| } | |
| } | |
| function StepAnimationCanvas(animation, value) { | |
| var endValue = animation.range[animation.range.length - 1]; | |
| var interp = animation.range[0] + (endValue - animation.range[0]) * (1 - value); | |
| if (animation.firstStep) { | |
| RemoveFromArray(visibleFinished, animation.element); | |
| animation.firstStep = false; | |
| } | |
| var changeAlpha = "opacity" == animation.attr; | |
| if (changeAlpha) { | |
| ctx.save(); | |
| ctx.globalAlpha = interp; | |
| } | |
| if (animation.func) { | |
| animation.func(ctx, interp, animation.inParams); | |
| } else { | |
| animation.element.func(ctx); | |
| } | |
| if (changeAlpha) { | |
| ctx.restore(); | |
| } | |
| } | |
| function StepAnimationSVG(animation, value) { | |
| var interp = animation.range[0] + (animation.range[1] - animation.range[0]) * (1 - value); | |
| switch (animation.attr) { | |
| case "opacity": | |
| animation.element.setAttribute(animation.attribute, interp); | |
| break; | |
| case "path": | |
| var attrStr = animation.func(interp, animation.inParams); | |
| animation.element.setAttribute(animation.attribute, attrStr); | |
| break; | |
| default: | |
| debugger; | |
| } | |
| } | |
| var animate_frame = 0; | |
| function AnimateList(now) { | |
| ++animate_frame; | |
| if (animationState.paused) { | |
| return; | |
| } | |
| if (animationState.start == null) { | |
| animationState.start = now - animationState.time; | |
| } | |
| animationState.time = now - animationState.start; | |
| var stillPending = []; | |
| for (var index in animationsPending) { | |
| var animation = animationsPending[index]; | |
| var interval = animationState.time - animation.timing[0]; | |
| if (interval <= 0) { | |
| stillPending.push(animation); | |
| continue; | |
| } | |
| animationsActive.push(animation); | |
| var inList = false; | |
| for (var dlIndex in displayList) { | |
| var displayable = displayList[dlIndex]; | |
| if (displayable == animation.element) { | |
| inList = true; | |
| break; | |
| } | |
| } | |
| if (!inList) { | |
| displayList.push(animation.element); | |
| } | |
| } | |
| animationsPending = stillPending; | |
| var stillAnimating = []; | |
| if ('Canvas' == animationState.displayEngine) { | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // for (var index in visibleFinished) { | |
| // var animation = visibleFinished[index]; | |
| // animation.endAnimation = false; | |
| // } | |
| } | |
| for (var index in animationsActive) { | |
| var animation = animationsActive[index]; | |
| var interval = animationState.time - animation.timing[0]; | |
| animation.remaining = animation.duration > interval ? animation.duration - interval : 0; | |
| animation.endAnimation = animation.remaining <= 0; | |
| if (animation.endAnimation) { | |
| switch (animationState.displayEngine) { | |
| case 'SVG': EndAnimationSVG(animation, visibleFinished); break; | |
| case 'Canvas': EndAnimationCanvas(animation, visibleFinished); break; | |
| default: debugger; | |
| } | |
| continue; | |
| } | |
| var value = animation.remaining / animation.duration; | |
| switch (animationState.displayEngine) { | |
| case 'SVG': StepAnimationSVG(animation, value); break; | |
| case 'Canvas': | |
| if (!animation.firstElement || !animation.firstElement.endAnimation) { | |
| StepAnimationCanvas(animation, value); | |
| } | |
| break; | |
| default: debugger; | |
| } | |
| stillAnimating.push(animation); | |
| } | |
| if ('Canvas' == animationState.displayEngine) { | |
| for (var index in visibleFinished) { | |
| var animation = visibleFinished[index]; | |
| if (!animation.endAnimation) { | |
| EndAnimationCanvas(animation, null); | |
| } | |
| } | |
| } | |
| animationsActive = stillAnimating; | |
| if (animationsPending.length || animationsActive.length) { | |
| animationState.requestID = requestAnimationFrame(AnimateList); | |
| } | |
| } | |
| function CancelAnimate(now) { | |
| if (animationState.start == null) { | |
| animationState.start = now; | |
| } | |
| var time = now - animationState.start; | |
| var stillAnimating = []; | |
| for (var index in animationsActive) { | |
| var animation = animationsActive[index]; | |
| var remaining = animation.remaining - time; | |
| var value = remaining / animation.duration; | |
| switch (animationState.displayEngine) { | |
| case 'SVG': animation.element.setAttribute(animation.attribute, value); break; | |
| case 'Canvas': break; | |
| } | |
| if (remaining <= 0) { | |
| continue; | |
| } | |
| stillAnimating.push(animation); | |
| } | |
| animationsActive = stillAnimating; | |
| if (animationsActive.length) { | |
| animationState.requestID = requestAnimationFrame(CancelAnimate); | |
| return; | |
| } | |
| animationsPending = []; | |
| animationState.reset(); | |
| if (keyFrameQueue.length > 0) { | |
| var animationFunc = keyFrameQueue.pop(); | |
| animationFunc(); | |
| } | |
| } | |
| function CancelAnimation() { | |
| cancelAnimationFrame(animationState.requestID); | |
| for (var index in animationsActive) { | |
| var animation = animationsActive[index]; | |
| switch (animation.attr) { | |
| case "opacity": | |
| var tmp = animation.range[0]; animation.range[0] = animation.range[1]; animation[1] = tmp; | |
| animation.remaining = animation.duration - animation.remaining; | |
| animation.remaining /= animation.duration / 1000; | |
| animation.duration = 1000; | |
| break; | |
| case "fadeOut": | |
| RemoveFromArray(visibleFinished, animation.element); | |
| break; | |
| case "path": | |
| break; | |
| default: | |
| debugger; | |
| } | |
| } | |
| for (var index in visibleFinished) { | |
| var animation = visibleFinished[index]; | |
| animation.action = "fadeOut"; | |
| animation.remaining = animation.duration = 1000; | |
| animationsActive.push(animation); | |
| } | |
| visibleFinished = []; | |
| animationState.reset(); | |
| animationState.requestID = requestAnimationFrame(CancelAnimate); | |
| } | |
| function PauseAnimation() { | |
| animationState.paused = true; | |
| } | |
| function QueueAnimation(animationFunc) { | |
| if (null == animationState.requestID) { | |
| animationFunc(); | |
| return; | |
| } | |
| keyFrameQueue.push(animationFunc); | |
| } | |
| function UnpauseAnimation() { | |
| animationState.paused = false; | |
| animationState.start = performance.now() - animationState.time; | |
| animationState.requestID = requestAnimationFrame(AnimateList); | |
| } | |
| function SetupTextSVG(t, x, y) { | |
| var text; | |
| if (typeof t == "string") { | |
| text = document.getElementById(t); | |
| } else { | |
| text = t; | |
| } | |
| text.setAttribute("font-family", "Helvetica,Arial"); | |
| text.setAttribute("font-size", "1.3rem"); | |
| if (typeof x == 'number') { | |
| text.setAttribute("x", x); | |
| } else if (null == text.getAttribute("x")) { | |
| text.setAttribute("x", 400); | |
| } | |
| if (typeof y == 'number') { | |
| text.setAttribute("y", y); | |
| } else if (null == text.getAttribute("y")) { | |
| text.setAttribute("y", 200); | |
| } | |
| } | |
| function SetupTextCanvas(t, x, y) { | |
| var text = typeof t == 'string' ? t : t.id; | |
| var record = canvasData[text]; | |
| if (typeof x == 'number') { | |
| record.x = x; | |
| } | |
| if (typeof y == 'number') { | |
| record.y = y; | |
| } | |
| record.position = canvas_xy(record); | |
| record.func = new Function('ctx', record.style + record.draw + record.position); | |
| } | |
| function SetupText(t, x, y) { | |
| switch (animationState.displayEngine) { | |
| case 'SVG': | |
| SetupTextSVG(t, x, y); | |
| break; | |
| case 'Canvas': | |
| SetupTextCanvas(t, x, y); | |
| break; | |
| default: | |
| debugger; | |
| } | |
| } | |
| function FirstText(text) { | |
| SetupText(text); | |
| AnimationAdd([0, 1000], text); | |
| } | |
| function EngineInit(keyframe) { | |
| displayList = []; | |
| switch (animationState.displayEngine) { | |
| case 'SVG': break; | |
| case 'Canvas': CanvasInit(keyframe); break; | |
| default: debugger; | |
| } | |
| } | |
| function EngineStart() { | |
| switch (animationState.displayEngine) { | |
| case 'SVG': break; | |
| case 'Canvas': | |
| // associate fadeIn and fadeOut | |
| for (var outerIndex in animationsPending) { | |
| var outer = animationsPending[outerIndex]; | |
| for (var innerIndex in animationsPending) { | |
| if (outerIndex == innerIndex) { | |
| continue; | |
| } | |
| var inner = animationsPending[innerIndex]; | |
| if (inner.element == outer.element) { | |
| inner.firstElement = outer; | |
| continue; | |
| } | |
| } | |
| } | |
| break; | |
| default: debugger; | |
| } | |
| animationState.reset(); | |
| animationState.requestID = requestAnimationFrame(AnimateList); | |
| } | |
| function AnimateSpanWedge() { | |
| EngineInit('keyframe1'); | |
| FirstText(spanWedgeDesc); | |
| AnimationAdd([1000, 2000], span1); | |
| AnimationAdd([1500, 3000], wedge1); | |
| AnimationAdd([3500, 4000], span1, [1, 0]); | |
| AnimationAdd([3500, 4000], wedge1, [1, 0]); | |
| AnimationAdd([4000, 5000], span2); | |
| AnimationAdd([4500, 6000], wedge2); | |
| AnimationAdd([6500, 7000], span2, [1, 0]); | |
| AnimationAdd([6500, 7000], wedge2, [1, 0]); | |
| AnimationAdd([7000, 8000], span3); | |
| AnimationAdd([7500, 9000], wedge3); | |
| EngineStart(); | |
| } | |
| function AnimateTrivialWedge() { | |
| EngineInit('keyframe2'); | |
| FirstText(trivialWedgeDesc1); | |
| FirstText(trivialWedgeDesc2); | |
| AnimationAdd([2000, 3500], span4); | |
| AnimationAdd([2000, 3500], wedge4); | |
| AnimationAdd([2000, 3500], span5); | |
| AnimationAdd([2000, 3500], wedge5); | |
| AnimationAdd([2000, 3500], span6); | |
| AnimationAdd([2000, 3500], wedge6); | |
| EngineStart(); | |
| } | |
| function AnimateSectorDesc() { | |
| EngineInit('keyframe3'); | |
| FirstText(sectorDesc1); | |
| FirstText(sectorDesc2); | |
| AnimationAdd([ 0, 1000], xaxis); | |
| AnimationAdd([ 500, 1500], yaxis); | |
| AnimationAdd([2000, 3500], sectorDescXYA); | |
| AnimationAdd([2000, 3500], wedgeXY8); | |
| AnimationAdd([3000, 4500], sectorDescXYB); | |
| AnimationAdd([3000, 4500], wedgeXY6); | |
| AnimationAdd([4000, 5500], sectorDescXYC); | |
| AnimationAdd([4000, 5500], wedgeXY3); | |
| EngineStart(); | |
| } | |
| function AnimateLineSingle() { | |
| EngineInit('keyframe4'); | |
| FirstText(lineSingleDesc); | |
| for (var i = 1; i <= 8; ++i) { | |
| SetupText("sectorDescXY" + i, 500, 260); | |
| } | |
| AnimationAdd([ 0, 1000], xaxis); | |
| AnimationAdd([ 0, 1000], yaxis); | |
| AnimationAdd([1000, 2000], lineSegment); | |
| AnimationAdd([1000, 3000], lineSegment, [-22.5 * Math.PI / 180], "path", evaluate_at, | |
| [ circle.center.x, circle.center.y, | |
| circle.center.x + " + " + circle.radius + " * Math.cos(value)", | |
| circle.center.y + " + " + circle.radius + " * Math.sin(value)", | |
| ]); | |
| AnimationAdd([2000, 3000], sectorDescXY1); | |
| AnimationAdd([2000, 3000], wedgeXY1); | |
| AnimationAdd([3000, 7000], lineSegment, [-22.5 * Math.PI / 180, (-22.5 - 360) * Math.PI / 180], | |
| "path", evaluate_at, | |
| [ circle.center.x, circle.center.y, | |
| circle.center.x + " + " + circle.radius + " * Math.cos(value)", | |
| circle.center.y + " + " + circle.radius + " * Math.sin(value)", | |
| ]); | |
| for (var i = 1; i < 8; ++i) { | |
| AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "sectorDescXY" + (i + 1)); | |
| AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "wedgeXY" + (i + 1)); | |
| AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "sectorDescXY" + i, [1, 0]); | |
| AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "wedgeXY" + i, [1, 0]); | |
| } | |
| AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], sectorDescXY1); | |
| AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], wedgeXY1); | |
| AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], sectorDescXY8, [1, 0]); | |
| AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], wedgeXY8, [1, 0]); | |
| EngineStart(); | |
| } | |
| function AnimateCurveMultiple() { | |
| EngineInit('keyframe5'); | |
| var cubicStart = PathDataArray(curveSegment1); | |
| var cubicMid = PathDataArray(curveSegment2); | |
| var cubicEnd = PathDataArray(curveSegment3); | |
| FirstText(curveMultipleDesc1); | |
| FirstText(curveMultipleDesc2); | |
| for (var i = 1; i <= 6; ++i) { | |
| SetupText("sectorDescXY" + i, 500, 260 + i * 25); | |
| } | |
| AnimationAdd([ 0, 1000], xaxis); | |
| AnimationAdd([ 0, 1000], yaxis); | |
| AnimationAdd([1000, 2000], curveSegment); | |
| AnimationAdd([2000, 3000], sectorDescXY1); | |
| AnimationAdd([2000, 3000], wedgeXY1); | |
| AnimationAdd([3000, 4000], curveSegment, [0, 1], "path", interpolate_at, [cubicStart, cubicMid]); | |
| AnimationAdd([4000, 5000], sectorDescXY2); | |
| AnimationAdd([4000, 5000], wedgeXY2); | |
| AnimationAdd([5000, 6000], curveSegment, [0, 1], "path", interpolate_at, [cubicMid, cubicEnd]); | |
| AnimationAdd([6000, 7000], sectorDescXY3); | |
| AnimationAdd([6000, 7000], wedgeXY3); | |
| AnimationAdd([6000, 7000], sectorDescXY4); | |
| AnimationAdd([6000, 7000], wedgeXY4); | |
| AnimationAdd([6000, 7000], sectorDescXY5); | |
| AnimationAdd([6000, 7000], wedgeXY5); | |
| AnimationAdd([6000, 7000], sectorDescXY6); | |
| AnimationAdd([6000, 7000], wedgeXY6); | |
| EngineStart(); | |
| } | |
| function AnimateOneDLines() { | |
| EngineInit('keyframe6'); | |
| FirstText(line1DDest1); | |
| FirstText(line1DDest2); | |
| for (var i = 9; i <= 11; ++i) { | |
| SetupText("sectorDescXY" + i, 500, 260 + (i - 8) * 25); | |
| } | |
| AnimationAdd([ 0, 1000], xaxis); | |
| AnimationAdd([ 0, 1000], yaxis); | |
| AnimationAdd([2000, 3000], sectorDescXY9); | |
| AnimationAdd([2000, 3000], horzSegment); | |
| AnimationAdd([3000, 4000], sectorDescXY10); | |
| AnimationAdd([3000, 4000], vertSegment); | |
| AnimationAdd([4000, 5000], sectorDescXY11); | |
| AnimationAdd([4000, 5000], diagSegment); | |
| EngineStart(); | |
| } | |
| function AnimateDiverging() { | |
| EngineInit('keyframe7'); | |
| var cubicData = PathDataArray(cubicSegment2); | |
| FirstText(curve1dDesc1); | |
| FirstText(curve1dDesc2); | |
| SetupText("sectorDescXY9", 500, 285); | |
| SetupText("sectorDescXY1", 500, 320); | |
| AnimationAdd([ 0, 1000], xaxis); | |
| AnimationAdd([ 0, 1000], yaxis); | |
| AnimationAdd([1900, 1900], cubicSegment); | |
| AnimationAdd([2000, 3000], cubicSegment, [0, 1], "path", cubic_partial, cubicData); | |
| AnimationAdd([2000, 3000], sectorDescXY9); | |
| AnimationAdd([2000, 3000], horzSegment); | |
| AnimationAdd([3000, 4000], sectorDescXY1); | |
| AnimationAdd([3000, 4000], wedgeXY1); | |
| EngineStart(); | |
| } | |
| circle.animate = AnimateCircle; | |
| circle.start = null; | |
| function AngleToPt(center, radius, degrees) { | |
| var radians = degrees * Math.PI / 180.0; | |
| return { | |
| x: center.x + (radius * Math.cos(radians)), | |
| y: center.y - (radius * Math.sin(radians)) | |
| }; | |
| } | |
| function PtsToSweep(pt1, pt2, center) { // unused | |
| return { | |
| start: 180 / Math.PI * Math.atan2(pt1.y - center.y, pt1.x - center.x), | |
| end: 180 / Math.PI * Math.atan2(pt2.y - center.y, pt2.x - center.x) | |
| }; | |
| } | |
| function ArcStr(center, radius, startAngle, endAngle) { | |
| var endPt = AngleToPt(center, radius, endAngle); | |
| var arcSweep = endAngle - startAngle <= 180 ? "0" : "1"; | |
| return ["A", radius, radius, 0, arcSweep, 0, endPt.x, endPt.y].join(" "); | |
| } | |
| function ArcStart(center, radius, startAngle, endAngle) { | |
| var startPt = AngleToPt(center, radius, startAngle); | |
| return [ startPt.x, startPt.y, ArcStr(center, radius, startAngle, endAngle) ].join(" "); | |
| } | |
| function MakeArc(arcStart) { | |
| return "M" + arcStart; | |
| } | |
| function MakeWedge(center, arcStart) { | |
| return ["M", center.x, center.y, "L", arcStart, "z"].join(" "); | |
| } | |
| function Animate(path, now, dur) { | |
| if (path.start == null) { | |
| path.start = now; | |
| // console.log("start=" + now); | |
| } | |
| if (now - path.start < dur) { | |
| requestAnimationFrame(path.animate); | |
| return true; | |
| } | |
| return false; | |
| } | |
| function AnimateCircle(now) { | |
| if (circle.start == null) { | |
| circleFill.setAttribute("fill-opacity", "0.3"); | |
| } | |
| var dur = 2 * 1000; | |
| var animating = Animate(circle, now, dur); | |
| // console.log("now=" + now + "circle.start=" + circle.start ) | |
| var pathStr = ArcStart(circle.center, circle.radius, 0, (now - circle.start) / (dur / 359.9)); | |
| circle.setAttribute("d", MakeArc(pathStr)); | |
| circleFill.setAttribute("d", MakeWedge(circle.center, pathStr)); | |
| if (!animating) { | |
| var delay = dur - (now - circle.start); | |
| setTimeout(CircleFinal, delay); | |
| } | |
| } | |
| function CircleFinal() { | |
| var firstHalf = ArcStart(circle.center, circle.radius, 0, 180); | |
| var secondHalf = ArcStr(circle.center, circle.radius, 180, 360); | |
| circle.setAttribute("d", "M" + firstHalf + secondHalf + "z"); | |
| circleFill.setAttribute("d", "M" + firstHalf + secondHalf + "z"); | |
| } | |
| var svgNS = "http://www.w3.org/2000/svg"; | |
| function CreateTextLabels() | |
| { | |
| for (var i = 0; i < 32; ++i) { | |
| var text = document.createElementNS(svgNS, "text"); | |
| var pt = AngleToPt(circle.center, circle.radius + 80, i * 360 / 32); | |
| text.setAttribute("id", "t" + i); | |
| text.setAttribute("x", pt.x); | |
| text.setAttribute("y", pt.y); | |
| text.setAttribute("text-anchor", "middle"); | |
| text.setAttribute("alignment-baseline", "mathematical"); | |
| var textNode = document.createTextNode(i); | |
| text.appendChild(textNode); | |
| document.getElementById("svg").appendChild(text); | |
| } | |
| } | |
| // CreateTextLabels(); | |
| var keyframeArray = [ | |
| AnimateSpanWedge, | |
| AnimateTrivialWedge, | |
| AnimateSectorDesc, | |
| AnimateLineSingle, | |
| AnimateCurveMultiple, | |
| AnimateOneDLines, | |
| AnimateDiverging, | |
| ]; | |
| var keyframeIndex = 3; // keyframeArray.length - 1; // normally 0 ; set to debug a particular frame | |
| function QueueKeyframe() { | |
| QueueAnimation(keyframeArray[keyframeIndex]); | |
| if (keyframeIndex < keyframeArray.length - 1) { | |
| ++keyframeIndex; | |
| } | |
| } | |
| var grads; | |
| var paths; | |
| var canvas; | |
| var ctx; | |
| function canvasSetup() { | |
| canvas = document.getElementById("canvas"); | |
| ctx = canvas ? canvas.getContext("2d") : null; | |
| assert(ctx); | |
| var resScale = animationState.resScale = window.devicePixelRatio ? window.devicePixelRatio : 1; | |
| var unscaledWidth = canvas.width; | |
| var unscaledHeight = canvas.height; | |
| canvas.width = unscaledWidth * resScale; | |
| canvas.height = unscaledHeight * resScale; | |
| canvas.style.width = unscaledWidth + 'px'; | |
| canvas.style.height = unscaledHeight + 'px'; | |
| if (resScale != 1) { | |
| ctx.scale(resScale, resScale); | |
| } | |
| grads = CanvasGrads(ctx); | |
| paths = CanvasPaths(ctx); | |
| } | |
| function Onload() { | |
| canvasSetup(); | |
| var startBtn = document.getElementById('startBtn'); | |
| var stopBtn = document.getElementById('stopBtn'); | |
| var resetBtn = document.getElementById('resetBtn'); | |
| startBtn.addEventListener('click', function(e) { | |
| e.preventDefault(); | |
| e.srcElement.innerText = "Next"; | |
| CancelAnimation(); | |
| QueueKeyframe(); | |
| }); | |
| stopBtn.addEventListener('click', function(e) { | |
| e.preventDefault(); | |
| if (!animationState.paused) { | |
| PauseAnimation(); | |
| e.srcElement.innerText = "Resume"; | |
| } else { | |
| UnpauseAnimation(); | |
| e.srcElement.innerText = "Pause"; | |
| } | |
| }); | |
| resetBtn.addEventListener('click', function(e) { | |
| e.preventDefault(); | |
| CancelAnimation(); | |
| keyframeIndex = 0; | |
| startBtn.innerText = "Start"; | |
| QueueKeyframe(); | |
| }); | |
| } | |
| </script> | |
| </head> | |
| <body onLoad="Onload()"> | |
| <div class="controls"> | |
| <button type="button" id="startBtn">Start</button> | |
| <button type="button" id="stopBtn">Pause</button> | |
| <button type="button" id="resetBtn">Restart</button> | |
| </div> | |
| <canvas id="canvas" width="800" height="500" /> | |
| </body> | |
| </html> |