svghmi/widget_circularslider.ysl2
author usveticic
Tue, 18 Aug 2020 11:42:28 +0200
branchsvghmi
changeset 3045 f6d428330e04
parent 3018 22b969b409b0
child 3062 9ec338a99a18
permissions -rw-r--r--
All widgets reworked to use widget class and animate function if needed
// widget_circuralslider.ysl2

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

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

            this.update_DOM(value, this.handle_elt);
        }

        update_DOM(value, elt){
            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);
            elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")");

            if(this.setpoint_elt != undefined){
                if(this.last_drag!= this.drag){
                    if(this.drag){
                        this.setpoint_elt.setAttribute("style", this.setpoint_style);
                    }else{
                        this.setpoint_elt.setAttribute("style", "display:none");
                    }
                    this.last_drag = this.drag;
                }
            }
        }

        on_release(evt) {
            window.removeEventListener("touchmove", this.on_bound_drag, true);
            window.removeEventListener("mousemove", this.on_bound_drag, true);

            window.removeEventListener("mouseup", this.bound_on_release, true)
            window.removeEventListener("touchend", this.bound_on_release, true);
            window.removeEventListener("touchcancel", this.bound_on_release, true);
            if(this.drag){
                this.drag = false;
            }
            this.update_position(evt);
        }

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

        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){
                    this.svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
                }
                else if(fiEnd<fi && fi<fiEnd+minMax){
                    this.svg_dist = this.range[1];
                }
                else{
                    this.svg_dist = this.range[0];
                }


                this.apply_hmi_value(0, Math.ceil(this.svg_dist));

                // update ghost cursor
                if(this.setpoint_elt != undefined){
                    this.request_animate();
                }
            }

        }

        animate(){
            this.update_DOM(this.svg_dist, this.setpoint_elt);
        }

        on_select(evt){
            this.drag = true;
            this.enTimer = true;
            window.addEventListener("touchmove", this.on_bound_drag, true);
            window.addEventListener("mousemove", this.on_bound_drag, true);

            window.addEventListener("mouseup", this.bound_on_release, true)
            window.addEventListener("touchend", this.bound_on_release, true);
            window.addEventListener("touchcancel", this.bound_on_release, 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];

            //bind functions
            this.bound_on_select = this.on_select.bind(this);
            this.bound_on_release = this.on_release.bind(this);
            this.on_bound_drag = this.on_drag.bind(this);

            //init events
            this.element.addEventListener("mousedown", this.bound_on_select);
            this.element.addEventListener("touchstart", this.bound_on_select);

            if(this.setpoint_elt != undefined){
                this.setpoint_style = this.setpoint_elt.getAttribute("style");
                this.setpoint_elt.setAttribute("style", "display:none");
            }


            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");
    |,
}