├── smiley-slider.png
├── README
├── LICENSE
├── index.html
└── smiley-slider.js
/smiley-slider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dglittle/smiley-slider/HEAD/smiley-slider.png
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | This is designed to let users rate how happy they are with something using a
2 | smiley faced slider.
3 |
4 | Use:
5 |
6 | // add the slider to a div with id "slider"
7 | var s = new SmileySlider(document.getElementById("slider"))
8 |
9 | s.position(0) // make it sad
10 | s.position(1) // make it happy
11 |
12 | var p = s.position() // get it's position
13 | s.position(p / 2) // make it half as happy
14 |
15 | s.position(function (p) {
16 | // do something when it changes
17 | })
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011 oDesk Corporation
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
9 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | smiley-slider
6 |
8 |
9 |
10 |
11 |
12 |
35 |
36 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/smiley-slider.js:
--------------------------------------------------------------------------------
1 |
2 | function SmileySlider(container, imgSrc) {
3 | if (!imgSrc)
4 | imgSrc = "smiley-slider.png"
5 |
6 | var width = 329
7 | var height = 37
8 |
9 | var headWidth = 40
10 | var maxHeadX = width - headWidth + 1
11 |
12 | var base = document.createElement('div')
13 | base.style.width = width + "px"
14 | base.style.height = height + "px"
15 | base.style.background = "white"
16 |
17 | var track = document.createElement('div')
18 | track.style.width = width + "px"
19 | track.style.height = 6 + "px"
20 | track.style.marginRight = '-' + track.style.width
21 | track.style.marginBottom = '-' + track.style.height
22 | track.style.position = "relative"
23 | track.style.top = 15 + "px"
24 | track.style.background = "url('" + imgSrc + "')"
25 | base.appendChild(track)
26 |
27 | var head = document.createElement('div')
28 | head.style.width = headWidth + "px"
29 | head.style.height = height + "px"
30 | head.style.marginRight = '-' + head.style.width
31 | head.style.marginBottom = '-' + head.style.height
32 | head.style.position = "relative"
33 | head.style.background = "url('" + imgSrc + "') scroll 0px -6px"
34 | base.appendChild(head)
35 |
36 | var face = document.createElement('canvas')
37 | face.style.width = 36 + "px"
38 | face.style.height = 37 + "px"
39 | face.style.position = "relative"
40 | face.style.left = 4 + "px"
41 | face.width = "36"
42 | face.height = "37"
43 | head.appendChild(face)
44 |
45 | var glass = document.createElement('div')
46 | glass.style.width = width + "px"
47 | glass.style.height = height + "px"
48 | glass.style.marginRight = '-' + glass.style.width
49 | glass.style.marginBottom = '-' + glass.style.height
50 | glass.style.position = "relative"
51 | base.appendChild(glass)
52 |
53 | container.appendChild(base)
54 |
55 | //////////////////////////////////////////////////////////////
56 | // head position
57 |
58 | var onHeadMove = null
59 |
60 | function positionInt(e) {
61 | if (e === undefined) {
62 | return getPos(head).x - getPos(base).x
63 | } else {
64 | head.style.left = Math.round(cap(e, 0, maxHeadX)) + "px"
65 | var p = position()
66 | drawFace(face, 100, p, 0.8)
67 | if (onHeadMove) onHeadMove(p)
68 | }
69 | }
70 |
71 | function position(e) {
72 | if (e === undefined) {
73 | return lerp(0, 0, maxHeadX, 1, positionInt())
74 | } else if (typeof(e) == "function") {
75 | onHeadMove = e
76 | } else {
77 | positionInt(lerp(0, 0, 1, maxHeadX, e))
78 | }
79 | }
80 |
81 | this.position = position
82 | setTimeout(function () {
83 | position(0.5)
84 | }, 0)
85 |
86 | //////////////////////////////////////////////////////////////
87 | // mouse
88 |
89 | glass.onmousedown = function (e) {
90 | e.preventDefault()
91 | var pos = getRelPos(glass, e)
92 |
93 | var grabX = headWidth / 2
94 | var headX = positionInt()
95 | if (pos.x >= headX && pos.x < headX + headWidth) {
96 | grabX = pos.x - headX
97 | }
98 |
99 | positionInt(pos.x - grabX)
100 |
101 | var oldMove = document.onmousemove
102 | document.onmousemove = function (e) {
103 | var pos = getRelPos(glass, e)
104 |
105 | positionInt(pos.x - grabX)
106 | }
107 |
108 | var oldUp = document.onmouseup
109 | document.onmouseup = function (e) {
110 | document.onmousemove = oldMove
111 | document.onmouseup = oldUp
112 | }
113 | }
114 |
115 | //////////////////////////////////////////////////////////////
116 | // touch
117 |
118 | glass.ontouchstart = function (e) {
119 | e.preventDefault()
120 | var pos = getRelPos(glass, e.touches[0])
121 |
122 | var grabX = headWidth / 2
123 | var headX = positionInt()
124 | if (pos.x >= headX && pos.x < headX + headWidth) {
125 | grabX = pos.x - headX
126 | }
127 |
128 | positionInt(pos.x - grabX)
129 |
130 | var oldMove = document.ontouchmove
131 | document.ontouchmove = function (e) {
132 | e.preventDefault();
133 | var pos = getRelPos(glass, e.touches[0])
134 | positionInt(pos.x - grabX)
135 | }
136 |
137 | var oldEnd = document.ontouchend;
138 | var oldCancel = document.ontouchcancel
139 | document.ontouchend = document.ontouchcancel = function (e) {
140 | document.ontouchmove = oldMove
141 | document.ontouchend = oldEnd
142 | document.ontouchcancel = oldCancel;
143 | }
144 | }
145 |
146 | //////////////////////////////////////////////////////////////
147 | // core drawing code
148 |
149 | var PI180 = Math.PI / 180;
150 |
151 | function drawFace(canvas, radius, emotion, innerScale) {
152 | emotion = Math.max(0, Math.min(1, emotion));
153 | var diam = radius * 2;
154 |
155 | var ctx = canvas.getContext('2d');
156 | ctx.clearRect (0, 0, diam, diam);
157 |
158 | ctx.beginPath();
159 | ctx.fillStyle = '#414084';
160 | drawSmile(ctx, 15.5, 20, innerScale, emotion);
161 | ctx.closePath();
162 | ctx.fill();
163 |
164 | ctx.beginPath();
165 | ctx.lineWidth = 1;
166 | ctx.strokeStyle = '#414084';
167 | drawEyeBrows(ctx, 9.5, 16, 23, 16, 7, 5, emotion);
168 | ctx.stroke();
169 | };
170 |
171 | function drawSmile(context, mainRadius, offsetY, innerScale, emotion) {
172 | var eased = 1 - easeInQuad(emotion, 0, 1, 1);
173 | var innerScale = innerScale - (eased * 0.4);
174 | var curveOffset = easeInCubic(emotion, 0.1, 0.6, 1);
175 | drawArc(context, mainRadius, offsetY, innerScale, emotion);
176 | drawArc(context, mainRadius, offsetY, innerScale, emotion, curveOffset, true);
177 | }
178 |
179 | function easeInCubic(t, b, c, d) {
180 | return c*(t/=d)*t*t + b;
181 | }
182 |
183 | function easeInQuad(t, b, c, d) {
184 | return c*(t/=d)*t + b;
185 | }
186 |
187 | function drawArc(context, mainRadius, offsetY, innerScale, emotion, curveOffset, reverseX) {
188 | curveOffset = (curveOffset === undefined) ? 0 : curveOffset;
189 |
190 | var innerRadius = mainRadius * innerScale;
191 | var pad = mainRadius - innerRadius;
192 | var diam = innerRadius * 2;
193 |
194 | var SEGS = 16;
195 |
196 | var theta = 360 / SEGS;
197 | var emoScale = (emotion - 0.5) * 2;
198 |
199 | var sides = [pad, pad + diam];
200 | var ct = [[0, 0], [0, 0]];
201 |
202 | ct[0][0] = innerRadius * Math.cos((theta * 3) * PI180) + pad ;
203 | ct[0][1] = innerRadius * Math.sin((theta * 3) * PI180) * emoScale + offsetY + pad - (curveOffset * mainRadius);
204 |
205 | ct[1][0] = innerRadius * Math.cos((theta * 5) * PI180) + pad + (innerRadius * 2);
206 | ct[1][1] = innerRadius * Math.sin((theta * 5) * PI180) * emoScale + offsetY + pad - (curveOffset * mainRadius);
207 |
208 | if (reverseX) {
209 | sides.reverse();
210 | ct.reverse();
211 | }
212 |
213 | context.moveTo(sides[0], offsetY + pad);
214 | context.bezierCurveTo(ct[0][0], ct[0][1], ct[1][0], ct[1][1], sides[1], offsetY + pad);
215 | }
216 |
217 | function drawEyeBrows(context, x1, y1, x2, y2, width, distance, emotion) {
218 | var a = (emotion - 0.5) * 30;
219 | var hW = width * 0.5;
220 |
221 | var l1 = rotZ(-hW, -distance, -a);
222 | var l2 = rotZ(hW , -distance, -a);
223 |
224 | var r1 = rotZ(-hW, -distance, a);
225 | var r2 = rotZ(hW , -distance, a);
226 |
227 | context.moveTo(l1[0] + x1, l1[1] + y1);
228 | context.lineTo(l2[0] + x1, l2[1] + y1);
229 |
230 | context.moveTo(r1[0] + x2, r1[1] + y2);
231 | context.lineTo(r2[0] + x2, r2[1] + y2);
232 | }
233 |
234 | function rotZ(x, y, angle) {
235 | var cos = Math.cos(angle * PI180);
236 | var sin = Math.sin(angle * PI180);
237 | var tx = x * cos - y * sin;
238 | var ty = x * sin + y * cos;
239 | return [tx, ty];
240 | }
241 |
242 | //////////////////////////////////////////////////////////////
243 | // utils
244 |
245 | function cap(t, mi, ma) {
246 | if (t < mi) return mi
247 | if (t > ma) return ma
248 | return t
249 | }
250 |
251 | function lerp(t0, v0, t1, v1, t) {
252 | return (t - t0) * (v1 - v0) / (t1 - t0) + v0
253 | }
254 |
255 | function getPos(e) {
256 | var x = 0, y = 0
257 | while (e != null) {
258 | x += e.offsetLeft
259 | y += e.offsetTop
260 | e = e.offsetParent
261 | }
262 | return {x : x, y : y}
263 | }
264 |
265 | function getRelPos(to, positionedObject) {
266 | var pos = getPos(to)
267 | return {
268 | x : positionedObject.pageX - pos.x,
269 | y : positionedObject.pageY - pos.y
270 | }
271 | }
272 | }
273 |
274 |
--------------------------------------------------------------------------------