Showing
2 changed files
with
153 additions
and
39 deletions
| ... | @@ -15,7 +15,7 @@ | ... | @@ -15,7 +15,7 @@ |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | /* | 17 | /* |
| 18 | - Alternate Sample module file to illustrate framework integration. | 18 | + Sample module file to illustrate framework integration. |
| 19 | 19 | ||
| 20 | @author Simon Hunt | 20 | @author Simon Hunt |
| 21 | */ | 21 | */ |
| ... | @@ -23,9 +23,24 @@ | ... | @@ -23,9 +23,24 @@ |
| 23 | (function (onos) { | 23 | (function (onos) { |
| 24 | 'use strict'; | 24 | 'use strict'; |
| 25 | 25 | ||
| 26 | - var svg; | 26 | + var pi = Math.PI, |
| 27 | - | 27 | + svg, |
| 28 | - | 28 | + dotG, |
| 29 | + nCircles = 12, | ||
| 30 | + circleData = [], | ||
| 31 | + dotId = 0, | ||
| 32 | + angle = 360 / nCircles, | ||
| 33 | + baseAngle = -90 - angle, | ||
| 34 | + groupRadius = 120, | ||
| 35 | + dotRadius = 24, | ||
| 36 | + dotMoveMs = 800, | ||
| 37 | + dotAppearMs = 300, | ||
| 38 | + dotEase = 'elastic', | ||
| 39 | + colorScale = d3.scale.linear() | ||
| 40 | + .domain([-pi/2, 2*pi/4, 3*pi/2]) | ||
| 41 | + .range(['green', 'goldenrod', 'blue']); | ||
| 42 | + | ||
| 43 | + // set the size of the SVG layer to match that of the view | ||
| 29 | function sizeSvg(view) { | 44 | function sizeSvg(view) { |
| 30 | svg.attr({ | 45 | svg.attr({ |
| 31 | width: view.width(), | 46 | width: view.width(), |
| ... | @@ -33,41 +48,140 @@ | ... | @@ -33,41 +48,140 @@ |
| 33 | }); | 48 | }); |
| 34 | } | 49 | } |
| 35 | 50 | ||
| 36 | - // NOTE: view is a view-token data structure: | ||
| 37 | - // { | ||
| 38 | - // vid: 'view-id', | ||
| 39 | - // nid: 'nav-id', | ||
| 40 | - // $div: ... // d3 selection of dom view div. | ||
| 41 | - // } | ||
| 42 | - | ||
| 43 | // gets invoked only the first time the view is loaded | 51 | // gets invoked only the first time the view is loaded |
| 44 | function preload(view, ctx) { | 52 | function preload(view, ctx) { |
| 53 | + // prepare our SVG layer... | ||
| 45 | svg = view.$div.append('svg'); | 54 | svg = view.$div.append('svg'); |
| 46 | sizeSvg(view); | 55 | sizeSvg(view); |
| 56 | + dotG = svg.append('g').attr('id', 'dots'); | ||
| 47 | } | 57 | } |
| 48 | 58 | ||
| 59 | + // gets invoked just before our view is loaded | ||
| 49 | function reset(view) { | 60 | function reset(view) { |
| 50 | - // clear our svg of all objects | 61 | + // clear dot group and reset circle data |
| 51 | - svg.html(''); | 62 | + dotG.html(''); |
| 63 | + circleData = []; | ||
| 64 | + // also clear text, if any | ||
| 65 | + svg.selectAll('text').remove(); | ||
| 52 | } | 66 | } |
| 53 | 67 | ||
| 54 | - function load(view, ctx) { | 68 | + function updateCirclePositions(view, addNew) { |
| 55 | - var fill = 'red', | 69 | + var w = view.width(), |
| 70 | + h = view.height(), | ||
| 71 | + ox = w / 2, | ||
| 72 | + oy = h / 2; | ||
| 73 | + | ||
| 74 | + // reposition existing dots | ||
| 75 | + circleData.forEach(function (c, i) { | ||
| 76 | + var inc = addNew ? 1 : 0, | ||
| 77 | + theta = ((i + inc) * angle + baseAngle) * pi/180, | ||
| 78 | + dx = Math.cos(theta) * groupRadius, | ||
| 79 | + dy = Math.sin(theta) * groupRadius, | ||
| 80 | + x = ox + dx, | ||
| 81 | + y = oy + dy; | ||
| 82 | + if (!addNew && i === 0) { | ||
| 83 | + x = ox; | ||
| 84 | + y = oy; | ||
| 85 | + } | ||
| 86 | + c.cx = x; | ||
| 87 | + c.cy = y; | ||
| 88 | + c.rgb = colorScale(theta); | ||
| 89 | + }); | ||
| 90 | + | ||
| 91 | + if (addNew) { | ||
| 92 | + // introduce a new dot | ||
| 93 | + circleData.unshift({ | ||
| 94 | + cx: ox, | ||
| 95 | + cy: oy, | ||
| 96 | + id: dotId++ | ||
| 97 | + }); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + // +1 to account for the circle in the center.. | ||
| 101 | + if (circleData.length > nCircles + 1) { | ||
| 102 | + circleData.splice(nCircles + 1, 1); | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + function doCircles(view) { | ||
| 107 | + var ox = view.width() / 2, | ||
| 108 | + oy = view.height() / 2, | ||
| 56 | stroke = 'black', | 109 | stroke = 'black', |
| 57 | - ctxText = ctx ? 'Context is "' + ctx + '"' : 'No Context'; | 110 | + fill = 'red', |
| 111 | + hoverFill = 'magenta'; | ||
| 112 | + | ||
| 113 | + // move existing circles, and add a new one | ||
| 114 | + updateCirclePositions(view, true); | ||
| 115 | + | ||
| 116 | + var circ = dotG.selectAll('circle') | ||
| 117 | + .data(circleData, function (d) { return d.id; }); | ||
| 118 | + | ||
| 119 | + // operate on existing elements | ||
| 120 | + circ.on('mouseover', null) | ||
| 121 | + .on('mouseout', null) | ||
| 122 | + .on('click', null) | ||
| 123 | + .transition() | ||
| 124 | + .duration(dotMoveMs) | ||
| 125 | + .ease(dotEase) | ||
| 126 | + .attr({ | ||
| 127 | + cx: function (d) { return d.cx; }, | ||
| 128 | + cy: function (d) { return d.cy; } | ||
| 129 | + }) | ||
| 130 | + .style({ | ||
| 131 | + cursor: 'default', | ||
| 132 | + fill: function (d) { return d.rgb; } | ||
| 133 | + }); | ||
| 58 | 134 | ||
| 59 | - svg.append('circle') | 135 | + // operate on entering elements |
| 136 | + circ.enter() | ||
| 137 | + .append('circle') | ||
| 60 | .attr({ | 138 | .attr({ |
| 61 | - cx: view.width() / 2, | 139 | + cx: function (d) { return d.cx; }, |
| 62 | - cy: view.height() / 2, | 140 | + cy: function (d) { return d.cy; }, |
| 63 | - r: 30 | 141 | + r: 0 |
| 64 | }) | 142 | }) |
| 65 | .style({ | 143 | .style({ |
| 66 | fill: fill, | 144 | fill: fill, |
| 67 | stroke: stroke, | 145 | stroke: stroke, |
| 68 | - 'stroke-width': 3.5 | 146 | + 'stroke-width': 3.5, |
| 69 | - }); | 147 | + cursor: 'pointer', |
| 148 | + opacity: 0 | ||
| 149 | + }) | ||
| 150 | + .on('mouseover', function (d) { | ||
| 151 | + d3.select(this).style('fill', hoverFill); | ||
| 152 | + }) | ||
| 153 | + .on('mouseout', function (d) { | ||
| 154 | + d3.select(this).style('fill', fill); | ||
| 155 | + }) | ||
| 156 | + .on('click', function (d) { | ||
| 157 | + setTimeout(function() { | ||
| 158 | + doCircles(view, true); | ||
| 159 | + }, 10); | ||
| 160 | + }) | ||
| 161 | + .transition() | ||
| 162 | + .delay(dotMoveMs) | ||
| 163 | + .duration(dotAppearMs) | ||
| 164 | + .attr('r', dotRadius) | ||
| 165 | + .style('opacity', 1); | ||
| 166 | + | ||
| 167 | + // operate on exiting elements | ||
| 168 | + circ.exit() | ||
| 169 | + .transition() | ||
| 170 | + .duration(750) | ||
| 171 | + .style('opacity', 0) | ||
| 172 | + .attr({ | ||
| 173 | + cx: ox, | ||
| 174 | + cy: oy, | ||
| 175 | + r: groupRadius - dotRadius | ||
| 176 | + }) | ||
| 177 | + .remove(); | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + function load(view, ctx) { | ||
| 181 | + var ctxText = ctx ? 'Context is "' + ctx + '"' : ''; | ||
| 70 | 182 | ||
| 183 | + // display our view context | ||
| 184 | + if (ctxText) { | ||
| 71 | svg.append('text') | 185 | svg.append('text') |
| 72 | .text(ctxText) | 186 | .text(ctxText) |
| 73 | .attr({ | 187 | .attr({ |
| ... | @@ -80,16 +194,23 @@ | ... | @@ -80,16 +194,23 @@ |
| 80 | }); | 194 | }); |
| 81 | } | 195 | } |
| 82 | 196 | ||
| 197 | + doCircles(view); | ||
| 198 | + } | ||
| 199 | + | ||
| 83 | function resize(view, ctx) { | 200 | function resize(view, ctx) { |
| 84 | sizeSvg(view); | 201 | sizeSvg(view); |
| 85 | - svg.selectAll('circle') | 202 | + updateCirclePositions(view); |
| 86 | - .attr({ | 203 | + |
| 87 | - cx: view.width() / 2, | 204 | + // move exiting dots into new positions, relative to view size |
| 88 | - cy: view.height() / 2 | 205 | + var circ = dotG.selectAll('circle') |
| 206 | + .data(circleData, function (d) { return d.id; }); | ||
| 207 | + circ.attr({ | ||
| 208 | + cx: function (d) { return d.cx; }, | ||
| 209 | + cy: function (d) { return d.cy; } | ||
| 89 | }); | 210 | }); |
| 90 | } | 211 | } |
| 91 | 212 | ||
| 92 | - // == register views here, with links to lifecycle callbacks | 213 | + // == register our view here, with links to lifecycle callbacks |
| 93 | 214 | ||
| 94 | onos.ui.addView('sample', { | 215 | onos.ui.addView('sample', { |
| 95 | preload: preload, | 216 | preload: preload, |
| ... | @@ -98,5 +219,4 @@ | ... | @@ -98,5 +219,4 @@ |
| 98 | resize: resize | 219 | resize: resize |
| 99 | }); | 220 | }); |
| 100 | 221 | ||
| 101 | - | ||
| 102 | }(ONOS)); | 222 | }(ONOS)); | ... | ... |
| ... | @@ -15,7 +15,7 @@ | ... | @@ -15,7 +15,7 @@ |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | /* | 17 | /* |
| 18 | - Sample module file to illustrate framework integration. | 18 | + Alternate sample module file to illustrate framework integration. |
| 19 | 19 | ||
| 20 | @author Simon Hunt | 20 | @author Simon Hunt |
| 21 | */ | 21 | */ |
| ... | @@ -33,13 +33,6 @@ | ... | @@ -33,13 +33,6 @@ |
| 33 | }); | 33 | }); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | - // NOTE: view is a view-token data structure: | ||
| 37 | - // { | ||
| 38 | - // vid: 'view-id', | ||
| 39 | - // nid: 'nav-id', | ||
| 40 | - // $div: ... // d3 selection of dom view div. | ||
| 41 | - // } | ||
| 42 | - | ||
| 43 | // gets invoked only the first time the view is loaded | 36 | // gets invoked only the first time the view is loaded |
| 44 | function preload(view, ctx) { | 37 | function preload(view, ctx) { |
| 45 | svg = view.$div.append('svg'); | 38 | svg = view.$div.append('svg'); |
| ... | @@ -52,8 +45,8 @@ | ... | @@ -52,8 +45,8 @@ |
| 52 | } | 45 | } |
| 53 | 46 | ||
| 54 | function load(view, ctx) { | 47 | function load(view, ctx) { |
| 55 | - var fill = 'blue', | 48 | + var fill = 'teal', |
| 56 | - stroke = 'grey'; | 49 | + stroke = 'black'; |
| 57 | 50 | ||
| 58 | svg.append('circle') | 51 | svg.append('circle') |
| 59 | .attr({ | 52 | .attr({ |
| ... | @@ -64,7 +57,8 @@ | ... | @@ -64,7 +57,8 @@ |
| 64 | .style({ | 57 | .style({ |
| 65 | fill: fill, | 58 | fill: fill, |
| 66 | stroke: stroke, | 59 | stroke: stroke, |
| 67 | - 'stroke-width': 3.5 | 60 | + 'stroke-width': 1.5, |
| 61 | + opacity: 0.5 | ||
| 68 | }); | 62 | }); |
| 69 | } | 63 | } |
| 70 | 64 | ... | ... |
-
Please register or login to post a comment