|
1 // widget_circuralslider.ysl2 |
|
2 |
|
3 template "widget[@type='CircularSlider']", mode="widget_class" |
|
4 || |
|
5 class CircularSliderWidget extends Widget{ |
|
6 frequency = 5; |
|
7 range = undefined; |
|
8 circle = undefined; |
|
9 handle_pos = undefined; |
|
10 drag = false; |
|
11 enTimer = false; |
|
12 |
|
13 dispatch(value) { |
|
14 if(!this.drag){ |
|
15 if(this.value_elt) |
|
16 this.value_elt.textContent = String(value); |
|
17 |
|
18 this.handle_position(value); |
|
19 } |
|
20 } |
|
21 |
|
22 handle_position(value){ |
|
23 let [min,max,totalDistance] = this.range; |
|
24 let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance))); |
|
25 let tip = this.range_elt.getPointAtLength(length); |
|
26 this.handle_elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")"); |
|
27 } |
|
28 |
|
29 on_release(evt) { |
|
30 if(this.drag){ |
|
31 this.drag = false; |
|
32 } |
|
33 } |
|
34 |
|
35 update_position(evt){ |
|
36 if(this.drag && this.enTimer){ |
|
37 var svg_dist = 0; |
|
38 |
|
39 //calculate center of widget in html |
|
40 // --TODO maybe it would be better to bind this part to window change size event ??? |
|
41 let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox; |
|
42 let [cX, cY,fiStart,fiEnd,minMax,x1,y1,width,height] = this.circle; |
|
43 let htmlCirc = this.range_elt.getBoundingClientRect(); |
|
44 let cxHtml = ((htmlCirc.right-htmlCirc.left)/(width)*(cX-x1))+htmlCirc.left; |
|
45 let cyHtml = ((htmlCirc.bottom-htmlCirc.top)/(height)*(cY-y1))+htmlCirc.top; |
|
46 |
|
47 |
|
48 //get mouse coordinates |
|
49 let mouseX = undefined; |
|
50 let mouseY = undefined; |
|
51 if (evt.type.startsWith("touch")){ |
|
52 mouseX = Math.ceil(evt.touches[0].clientX); |
|
53 mouseY = Math.ceil(evt.touches[0].clientY); |
|
54 } |
|
55 else{ |
|
56 mouseX = evt.pageX; |
|
57 mouseY = evt.pageY; |
|
58 } |
|
59 |
|
60 //calculate angle |
|
61 let fi = Math.atan2(cyHtml-mouseY, mouseX-cxHtml); |
|
62 |
|
63 // transform from 0 to 2PI |
|
64 if (fi > 0){ |
|
65 fi = 2*Math.PI-fi; |
|
66 } |
|
67 else{ |
|
68 fi = -fi; |
|
69 } |
|
70 |
|
71 //offset it to 0 |
|
72 fi = fi - fiStart; |
|
73 if (fi < 0){ |
|
74 fi = fi + 2*Math.PI; |
|
75 } |
|
76 |
|
77 //get handle distance from mouse position |
|
78 if(fi<fiEnd){ |
|
79 svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]); |
|
80 } |
|
81 else if(fiEnd<fi && fi<fiEnd+minMax){ |
|
82 svg_dist = this.range[1]; |
|
83 } |
|
84 else{ |
|
85 svg_dist = this.range[0]; |
|
86 } |
|
87 |
|
88 //redraw handle --TODO is it fast enough if I just call change_hmi_value??? |
|
89 this.handle_position(svg_dist); |
|
90 if(this.value_elt) |
|
91 this.value_elt.textContent = String(Math.ceil(svg_dist)); |
|
92 this.apply_hmi_value(0, Math.ceil(svg_dist)); |
|
93 |
|
94 //reset timer |
|
95 this.enTimer = false; |
|
96 setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); |
|
97 } |
|
98 |
|
99 } |
|
100 |
|
101 on_select(evt){ |
|
102 this.drag = true; |
|
103 this.enTimer = true; |
|
104 this.update_position(evt); |
|
105 } |
|
106 |
|
107 init() { |
|
108 //get min max |
|
109 let min = this.min_elt ? |
|
110 Number(this.min_elt.textContent) : |
|
111 this.args.length >= 1 ? this.args[0] : 0; |
|
112 let max = this.max_elt ? |
|
113 Number(this.max_elt.textContent) : |
|
114 this.args.length >= 2 ? this.args[1] : 100; |
|
115 |
|
116 //fiStart ==> offset |
|
117 let fiStart = Number(this.range_elt.getAttribute('sodipodi:start')); |
|
118 let fiEnd = Number(this.range_elt.getAttribute('sodipodi:end')); |
|
119 fiEnd = fiEnd - fiStart; |
|
120 |
|
121 //fiEnd ==> size of angle |
|
122 if (fiEnd < 0){ |
|
123 fiEnd = 2*Math.PI + fiEnd; |
|
124 } |
|
125 |
|
126 //min max barrier angle |
|
127 let minMax = (2*Math.PI - fiEnd)/2; |
|
128 |
|
129 //get parameters from svg |
|
130 let cX = Number(this.range_elt.getAttribute('sodipodi:cx')); |
|
131 let cY = Number(this.range_elt.getAttribute('sodipodi:cy')); |
|
132 this.range_elt.style.strokeMiterlimit="0"; //eliminates some weird border around html object |
|
133 this.range = [min, max,this.range_elt.getTotalLength()]; |
|
134 let cPos = this.range_elt.getBBox(); |
|
135 this.handle_pos = this.range_elt.getPointAtLength(0); |
|
136 this.circle = [cX, cY,fiStart,fiEnd,minMax,cPos.x,cPos.y,cPos.width,cPos.height]; |
|
137 |
|
138 //init events |
|
139 this.handle_elt.addEventListener("touchstart", hmi_widgets[this.element_id].on_select.bind(this)); |
|
140 this.handle_elt.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this)); |
|
141 this.element.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this)); |
|
142 |
|
143 window.addEventListener("touchmove", hmi_widgets[this.element_id].update_position.bind(this)); |
|
144 window.addEventListener("mousemove", hmi_widgets[this.element_id].update_position.bind(this)); |
|
145 |
|
146 window.addEventListener("mouseup", hmi_widgets[this.element_id].on_release.bind(this)) |
|
147 window.addEventListener("touchend", hmi_widgets[this.element_id].on_release.bind(this)); |
|
148 window.addEventListener("touchcancel", hmi_widgets[this.element_id].on_release.bind(this)); |
|
149 |
|
150 } |
|
151 } |
|
152 || |
|
153 |
|
154 template "widget[@type='CircularSlider']", mode="widget_defs" { |
|
155 param "hmi_element"; |
|
156 labels("handle range"); |
|
157 optional_labels("value min max"); |
|
158 |, |
|
159 } |