Mercurial > code > home > repos > light9
view light9/web/timeline-elements.html @ 1315:b21e31c79f31
more timeline drawing code. cool zoom zigzag feature
Ignore-this: 8f8f8d1c2d8ab6286d1a0efa0323a9b2
author | Drew Perttula <drewp@bigasterisk.com> |
---|---|
date | Thu, 02 Jun 2016 06:40:12 +0000 |
parents | c91d1044fe49 |
children | 4c6d88aa9e26 |
line wrap: on
line source
<link rel="import" href="/lib/polymer/polymer.html"> <link rel="import" href="light9-timeline-audio.html"> <link rel="import" href="/lib/iron-resizable-behavior/iron-resizable-behavior.html"> <!-- Whole editor- include this on your page. Most coordinates are relative to this element. --> <dom-module id="light9-timeline-editor"> <template> <style> :host { background: #444; display: flex; flex-direction: column; position: relative; border: 1px solid black; } light9-timeline-audio { width: 100%; height: 30px; } light9-timeline-time-zoomed { flex-grow: 1; } light9-timeline-diagram-layer, light9-timeline-adjusters { position: absolute; left: 0; top: 0; right: 0; bottom: 0; } </style> <div> timeline editor: song [uri] <button>unlink</button> <label><input type="checkbox"> follow player song choice</label> </div> <!-- Old zoom menu: See current time .. esc See from current time -> end .. shift+esc Zoom all .. ctrl+esc --> <light9-timeline-audio id="audio"></light9-timeline-audio> <!-- should have a zoom-out cursor on it, or zigzag the one cursor --> <light9-timeline-time-zoomed id="zoomed" zoom="{{viewState.zoomSpec}}"></light9-timeline-time-zoomed> <light9-timeline-diagram-layer id="dia"></light9-timeline-diagram-layer> <light9-timeline-adjusters id="adjusters"></light9-timeline-adjusters> </template> <script> Polymer({ is: "light9-timeline-editor", behaviors: [ Polymer.IronResizableBehavior ], properties: { viewState: {type: Object} }, ready: function() { this.viewState = { // anything not persisted in the model zoomSpec: { duration: 190, t1: 102, t2: 161 }, cursor: { t: 105, } }; setInterval(function() { this.viewState.cursor.t = 130 + 20 * Math.sin(Date.now() / 2000); this.$.dia.setCursor(this.$.audio.offsetTop, this.$.audio.offsetHeight, this.$.zoomed.$.time.offsetTop, this.$.zoomed.$.time.offsetHeight, this.viewState.zoomSpec, this.viewState.cursor); }.bind(this), 50); } }); </script> </dom-module> <!-- the whole section that pans/zooms in time (most of the editor) --> <dom-module id="light9-timeline-time-zoomed"> <template> <style> :host { display: flex; height: 100%; } div { display: flex; flex-direction: column; height: 100%; } light9-timeline-cursor { width: 0; height: 100%; z-index: 1; } light9-timeline-audio { width: 100%; height: 90px; } light9-timeline-graph-row { flex-grow: 1; } </style> <div> <light9-timeline-time-axis id="time"></light9-timeline-time-axis> <light9-timeline-audio zoom="{{zoom}}"></light9-timeline-audio> <template is="dom-repeat" items="{{rows}}"> <light9-timeline-graph-row></light9-timeline-graph-row> </template> </div> </template> <script> Polymer({ is: "light9-timeline-time-zoomed", behaviors: [ Polymer.IronResizableBehavior ], properties: { rows: {value: [0, 1, 2, 3]}, zoom: {type: Object, notify: true} } }); </script> </dom-module> <!-- SVG or canvas that draws these: - background grids - zoom arcs - notes - annotations on notes - connectors between adjusters and their targets This element is not responsible for any hit detection. Things you click (rows, notes, adjusters, etc) are caught on their respective elements. (But is that wrong in the case of notes?) --> <dom-module id="light9-timeline-diagram-layer"> <template> <style> svg { width: 100%; height: 100%; } </style> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="0 0 1021 600" > <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1"> <path style="display:inline;fill:#758f8a;fill-opacity:0.32421055;fill-rule:evenodd;stroke:none;stroke-width:1px;" d="m -559.27177,395.81 2.22362,-54.79762 C -204.84146,338.25666 -8.4912266,336.03416 0.40325327,297.52215 9.8280633,285.70343 40.880483,289.65152 49.016853,296.10794 c 20.15718,42.1653 270.720527,47.21149 589.526257,46.31865 l -4.2185,53.3834 z" id="path4837" sodipodi:nodetypes="ccccccc" /> <path style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.30736844" d="M 0.75681338,294.44118 C -5.8919466,328.78953 -182.9094,341.3924 -555.73623,340.04956" id="path4862" sodipodi:nodetypes="cc" /> <path sodipodi:nodetypes="cc" id="path4864" d="m 48.622093,293.73407 c 6.6488,34.34835 183.666187,46.95123 556.493117,45.60839" style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.30736844" /> <path style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;" d="m -570.58547,460.25772 1210.56688,0" id="path5354"/> <path style="fill:#53774b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.50000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m -393.10167,652.33195 70.00357,-68.30517 169.70563,0 161.5297134,68.30517" id="note1" sodipodi:nodetypes="cccc" /> <rect style="color:#000000;display:inline;overflow:visible;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:0.12637365;fill-rule:evenodd;stroke:none;stroke-width:1.10000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" id="note1Attrs" width="108" height="47" x="-234.38405" y="598.8988" rx="0" ry="0" /> <path id="path4246" d="m -153.53195,562.60592 0,19.81802" style="opacity:0.21600001;fill:none;fill-rule:evenodd;stroke:#d4d4d4;stroke-width:0.92825276;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2.78475824, 2.78475824;stroke-dashoffset:0;stroke-opacity:1" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13px;line-height:125%;font-family:'Verana Sans';-inkscape-font-specification:'Verana Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;" x="-338.38403" y="631.3988" id="text4290" sodipodi:linespacing="125%" ><tspan sodipodi:role="line" id="tspan4292" x="-338.38403" y="631.3988">spotchase</tspan></text> <rect style="color:#000000;display:inline;overflow:visible;solid-color:#000000;solid-opacity:1;fill:#1ed8ec;fill-opacity:0.53846154;fill-rule:evenodd;stroke:#f2ff0e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2, 2;stroke-dashoffset:0;stroke-opacity:1;" id="rect4294" width="22.5" height="17" x="-224.88405" y="603.8988" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13px;line-height:125%;font-family:'Verana Sans';-inkscape-font-specification:'Verana Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;" x="-192.88405" y="615.8988" id="text4296" sodipodi:linespacing="125%" ><tspan sodipodi:role="line" id="tspan4298" x="-192.88405" y="615.8988">color</tspan></text> <rect y="620.97205" x="-224.98763" height="17" width="22.5" id="rect4304" style="color:#000000;display:inline;overflow:visible;solid-color:#000000;solid-opacity:1;fill:#1ed8ec;fill-opacity:0;fill-rule:evenodd;stroke:#f2ff0e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2, 2;stroke-dashoffset:0;stroke-opacity:1;" /> <g id="g4310" transform="translate(-880.47502,196.64838)" > <circle r="6" cy="432.78705" cx="666.625" id="path4306" style="color:#000000;display:inline;overflow:visible;solid-color:#000000;solid-opacity:1;fill:#1ed8ec;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:1.10000014;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" /> <circle style="color:#000000;display:inline;overflow:visible;solid-color:#000000;solid-opacity:1;fill:#1ed8ec;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:1.10000014;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" id="circle4308" cx="666.625" cy="432.78705" r="3.6249998" /> </g> <path style="opacity:0.21600001;fill:none;fill-rule:evenodd;stroke:#d4d4d4;stroke-width:0.92825276;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2.78475822, 2.78475822;stroke-dashoffset:0;stroke-opacity:1" d="m 7.2180534,637.60592 0,13.56802" id="path4314"/> <path id="path4332" d="m -393.38403,639.64882 0,12.75" style="opacity:0.21600001;fill:none;fill-rule:evenodd;stroke:#d4d4d4;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.00000026, 3.00000026;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:1;fill:none;fill-rule:evenodd;stroke:#d4d4d4;stroke-width:0.9282527;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2.78475821, 2.78475821;stroke-dashoffset:0;stroke-opacity:1" d="m -22.190957,301.51493 22.56802038,0" id="adjConnector"/> <path sodipodi:nodetypes="cccc" id="path4893" d="m -568.94472,458.6374 2.17576,-52.2338 1205.37474,0 1.5058,52.2338" style="fill:#216ca1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.50000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13px;line-height:125%;font-family:'Verana Sans';-inkscape-font-specification:'Verana Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;" x="-535.36261" y="433.71609" id="noteLabel" sodipodi:linespacing="125%" ><tspan sodipodi:role="line" id="tspan4902" x="-535.36261" y="433.71609">song4</tspan></text> <g id="noteAttrs" transform="translate(-930.38403,193.23677)" > <rect style="color:#000000;display:inline;overflow:visible;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:0.12637365;fill-rule:evenodd;stroke:none;stroke-width:1.10000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" id="rect4904" width="108" height="33" x="454.5" y="223.66205" rx="0" ry="0" /> <rect style="color:#000000;display:inline;overflow:visible;solid-color:#000000;solid-opacity:1;fill:#1ed8ec;fill-opacity:0.53846154;fill-rule:evenodd;stroke:#f2ff0e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2, 2;stroke-dashoffset:0;stroke-opacity:1;" id="rect4906" width="22.5" height="17" x="467" y="230.66205" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13px;line-height:125%;font-family:'Verana Sans';-inkscape-font-specification:'Verana Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;" x="499" y="242.66205" id="text4908" sodipodi:linespacing="125%"><tspan sodipodi:role="line" id="tspan4910" x="499" y="242.66205">color</tspan></text> </g> <g id="notes"> </g> <g id="cursor"> <path id="cursor1" style="fill:none; stroke:#ff0303; stroke-width:1.5; stroke-linecap:butt;" /> <path id="cursor2" style="fill:#9c0303;" /> <path id="cursor3" style="fill:none; stroke:#ff0303; stroke-width:3; stroke-linecap:butt;" /> </g> </g> </svg> </template> <script> function svgPathFromPoints(pts) { var out = ""; pts.forEach(function(p) { if (out.length == 0) { out = 'M '; } else { out += 'L '; } out += '' + p[0] + ',' + p[1] + ' '; }); return out; } Polymer({ is: "light9-timeline-diagram-layer", properties: { }, ready: function() { window.setNote = this.setNote.bind(this); this.cursorPath = { top: this.querySelector("#cursor1"), mid: this.querySelector("#cursor2"), bot: this.querySelector("#cursor3") }; }, setNote: function(uri, x1, x2, y1, y2) { console.log('set', uri, x1, x2, y1, y2); var d = svgPathFromPoints([[x1, y2], [x1*.75 + x2*.25, y1], [x1*.25 + x2*.75, y1], [x2, y2]]); var s = '<path style="fill:#53774b;fill-opacity:1;stroke:#000000;stroke-width:1.50000012;" d="' + d + '"/>'; console.log(s); this.$.notes.innerHTML += s; }, setCursor: function(y1, h1, y2, h2, zoom, cursor) { var xZoomedOut = this.offsetParent.offsetWidth * cursor.t / zoom.duration; var xZoomedIn = this.offsetParent.offsetWidth * (cursor.t - zoom.t1) / (zoom.t2 - zoom.t1); // replace with d3 scale this.cursorPath.top.setAttribute('d', svgPathFromPoints([[xZoomedOut, y1], [xZoomedOut, y1 + h1]])); this.cursorPath.mid.setAttribute('d', svgPathFromPoints([ [xZoomedIn + 2, y2 + h2], [xZoomedIn - 2, y2 + h2], [xZoomedOut - 1, y1 + h1], [xZoomedOut + 1, y1 + h1], ]) + ' Z'); this.cursorPath.bot.setAttribute('d', svgPathFromPoints([[xZoomedIn, y2 + h2], [xZoomedIn, this.offsetParent.offsetHeight]])); } }); </script> </dom-module> <!-- seconds labels --> <dom-module id="light9-timeline-time-axis"> <template> <pre>0 sec 10 20 30 40 50</pre> </template> <script> Polymer({ is: "light9-timeline-time-axis", properties: { } }); </script> </dom-module> <!-- one row of notes --> <dom-module id="light9-timeline-graph-row"> <template> <style> :host { border-top: 1px solid black; display: flex; } </style> <template is="dom-repeat" items="[1,2,3]"> <light9-timeline-note></light9-timeline-note> </template> </template> <script> Polymer({ is: "light9-timeline-graph-row", behaviors: [ Polymer.IronResizableBehavior ], properties: { } }); </script> </dom-module> <!-- playback cursor on top of everything. Maybe other cursors too: hover, snap, etc --> <dom-module id="light9-timeline-cursor"> <template> <style> :host { /* parent has arranged for us to be 0 wide and the proper height */ } div { background: red; position: relative; height: 100%; width: 3px; box-shadow: 3px 0 5px rgba(0, 0, 0, 0.34); } </style> <div style="left: {{x}}px"></div> </template> <script> Polymer({ is: "light9-timeline-cursor", properties: { x: {type: Number} }, ready: function () { setInterval(function() { this.x = 50 + 35 * Math.sin(Date.now()/4); }.bind(this), 50); } }); </script> </dom-module> <!-- One trapezoid note shape in a row. This element has the right Y coords. We compute X coords from the zoom setting. diagram-layer draws the note body. --> <dom-module id="light9-timeline-note"> <template> <style> :host { display: block; background: green; outline: 2px solid red; } </style> <light9-timeline-note-inline-attrs></light9-timeline-note-inline-attrs> </template> <script> window.xserial = 0; Polymer({ is: "light9-timeline-note", behaviors: [ Polymer.IronResizableBehavior ], listeners: { 'iron-resize': '_onIronResize' }, properties: { }, _onIronResize: function() { setNote('myuri', 60 + 150 * (window.xserial++), 180, this.offsetTop, this.offsetTop + this.offsetHeight); } }); </script> </dom-module> <!-- All the adjusters you can edit or select. This element manages their layout and suppresion. Owns the selection. Maybe includes selecting things that don't even have adjusters. Maybe manages the layout of other labels and text too, to avoid overlaps. --> <dom-module id="light9-timeline-adjusters"> <template> <style> :host { pointer-events: none; } </style> <light9-timeline-adjuster center="{x: 50, y: 200}"></light9-timeline-adjuster> <light9-timeline-adjuster center="{x: 90, y: 200}"></light9-timeline-adjuster> </template> <script> Polymer({ is: "light9-timeline-adjusters", properties: { } }); </script> </dom-module> <!-- Yellow dotted handle that you can adjust to edit something. Knows an attribute to edit and the true screen location, even if parent <light9-timeline-adjusters> has offset us a bit to avoid a text overlap. Draws affordance arrows and a connector line if we're far away from the point that we edit. May grow to a bigger editor when you click or drag. --> <dom-module id="light9-timeline-adjuster"> <template> <style> table { position: absolute; z-index: 2; border-collapse: collapse; } td { text-align: center; font-size: 20px; } span { font-size: 16px; background: rgba(255, 255, 0, 0.5); border: 3px yellow dotted; border-radius: 8px; padding: 5px; } </style> <table style="left: {{center.x}}px; top: {{center.y}}px"> <tr><td></td><td>↑</td><td></td></tr> <tr><td>←</td><td><span>{{value}}</span></td><td>→</td></tr> <tr><td></td><td>↓</td><td></td></tr> </table> </template> <script> Polymer({ is: "light9-timeline-adjuster", properties: { center: {type: Object, notify: true}, target: {type: Object, notify: true}, value: {value: '0.26'} } }); </script> </dom-module> <!-- sometimes we draw attrs within the shape of a note. --> <dom-module id="light9-timeline-note-inline-attrs"> <template> </template> <script> Polymer({ is: "light9-timeline-note-inline-attrs", properties: { } }); </script> </dom-module>