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>