svghmi/widget_circularslider.ysl2
author Edouard Tisserant
Thu, 20 Aug 2020 13:56:21 +0200
branchsvghmi
changeset 3031 440d74319a74
parent 3018 22b969b409b0
child 3045 f6d428330e04
permissions -rw-r--r--
SVGHMI: Refactor the way JsonTable generate javascript code to access json data. Now support multiple assignments, used in the case of text to change both content and style on the same element.
// widget_circuralslider.ysl2

template "widget[@type='CircularSlider']", mode="widget_class"
    ||
    class CircularSliderWidget extends Widget{
        frequency = 5;
        range = undefined;
        circle = undefined;
        handle_pos = undefined;
        drag = false;
        enTimer = false;

        dispatch(value) {
            if(!this.drag){
                if(this.value_elt)
                    this.value_elt.textContent = String(value);

                this.handle_position(value);
            }
        }

        handle_position(value){
            let [min,max,totalDistance] = this.range;
            let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance)));
            let tip = this.range_elt.getPointAtLength(length);
            this.handle_elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")");
        }

        on_release(evt) {
            if(this.drag){
                this.drag = false;
            }
        }

        update_position(evt){
            if(this.drag && this.enTimer){
                var svg_dist = 0;

                //calculate center of widget in html
                // --TODO maybe it would be better to bind this part to window change size event ???
                let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox;
                let [cX, cY,fiStart,fiEnd,minMax,x1,y1,width,height] = this.circle;
                let htmlCirc = this.range_elt.getBoundingClientRect();
                let cxHtml = ((htmlCirc.right-htmlCirc.left)/(width)*(cX-x1))+htmlCirc.left;
                let cyHtml = ((htmlCirc.bottom-htmlCirc.top)/(height)*(cY-y1))+htmlCirc.top;


                //get mouse coordinates
                let mouseX = undefined;
                let mouseY = undefined;
                if (evt.type.startsWith("touch")){
                    mouseX = Math.ceil(evt.touches[0].clientX);
                    mouseY = Math.ceil(evt.touches[0].clientY);
                }
                else{
                    mouseX = evt.pageX;
                    mouseY = evt.pageY;
                }

                //calculate angle
                let fi = Math.atan2(cyHtml-mouseY, mouseX-cxHtml);

                // transform from 0 to 2PI
                if (fi > 0){
                    fi = 2*Math.PI-fi;
                }
                else{
                    fi = -fi;
                }

                //offset it to 0
                fi = fi - fiStart;
                if (fi < 0){
                    fi = fi + 2*Math.PI;
                }

                //get handle distance from mouse position
                if(fi<fiEnd){
                    svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
                }
                else if(fiEnd<fi && fi<fiEnd+minMax){
                    svg_dist = this.range[1];
                }
                else{
                    svg_dist = this.range[0];
                }

                //redraw handle --TODO is it fast enough if I just call change_hmi_value???
                this.handle_position(svg_dist);
                if(this.value_elt)
                    this.value_elt.textContent = String(Math.ceil(svg_dist));
                this.apply_hmi_value(0, Math.ceil(svg_dist));

                //reset timer
                this.enTimer = false;
                setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
            }

        }

        on_select(evt){
            this.drag = true;
            this.enTimer = true;
            this.update_position(evt);
        }

        init() {
            //get min max
            let min = this.min_elt ?
                        Number(this.min_elt.textContent) :
                        this.args.length >= 1 ? this.args[0] : 0;
            let max = this.max_elt ?
                        Number(this.max_elt.textContent) :
                        this.args.length >= 2 ? this.args[1] : 100;

            //fiStart ==> offset
            let fiStart = Number(this.range_elt.getAttribute('sodipodi:start'));
            let fiEnd = Number(this.range_elt.getAttribute('sodipodi:end'));
            fiEnd = fiEnd - fiStart;

            //fiEnd ==> size of angle
            if (fiEnd < 0){
                fiEnd = 2*Math.PI + fiEnd;
            }

            //min max barrier angle
            let minMax = (2*Math.PI - fiEnd)/2;

            //get parameters from svg
            let cX = Number(this.range_elt.getAttribute('sodipodi:cx'));
            let cY = Number(this.range_elt.getAttribute('sodipodi:cy'));
            this.range_elt.style.strokeMiterlimit="0"; //eliminates some weird border around html object
            this.range = [min, max,this.range_elt.getTotalLength()];
            let cPos = this.range_elt.getBBox();
            this.handle_pos = this.range_elt.getPointAtLength(0);
            this.circle = [cX, cY,fiStart,fiEnd,minMax,cPos.x,cPos.y,cPos.width,cPos.height];

            //init events
            this.handle_elt.addEventListener("touchstart", hmi_widgets[this.element_id].on_select.bind(this));
            this.handle_elt.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));
            this.element.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));

            window.addEventListener("touchmove", hmi_widgets[this.element_id].update_position.bind(this));
            window.addEventListener("mousemove", hmi_widgets[this.element_id].update_position.bind(this));

            window.addEventListener("mouseup", hmi_widgets[this.element_id].on_release.bind(this))
            window.addEventListener("touchend", hmi_widgets[this.element_id].on_release.bind(this));
            window.addEventListener("touchcancel", hmi_widgets[this.element_id].on_release.bind(this));

        }
    }
    ||

template "widget[@type='CircularSlider']", mode="widget_defs" {
    param "hmi_element";
    labels("handle range");
    optional_labels("value min max");
    |,
}