beremiz

Button fix if no active or inactive state,
svghmi
2020-10-01, usveticic
9ec338a99a18
Button fix if no active or inactive state,
Widget animate changed to use anitmateTransform and added option to change rotation
Widget circular slider fixed so it is working on got and reprogramed so it similar to normal slider
Widget slider added support for changing size still need some changes to work properly
Added slider to svghmi test project
Changed svg in svhgmi_v2 project
// widget_slider.ysl2
template "widget[@type='Slider']", mode="widget_class"
||
class SliderWidget extends Widget{
frequency = 5;
range = undefined;
handle_orig = undefined;
scroll_size = undefined;
scroll_range = 0;
scroll_visible = 7;
min_size = 0.07;
fi = undefined;
curr_value = 0;
drag = false;
enTimer = false;
handle_click = undefined;
last_drag = false;
dispatch(value,oldval, index) {
if (index == 0){
let [min,max,start,totallength] = this.range;
//save current value inside widget
this.curr_value = value;
//check if in range
if (this.curr_value > max){
this.curr_value = max;
this.apply_hmi_value(0, this.curr_value);
}
else if (this.curr_value < min){
this.curr_value = min;
this.apply_hmi_value(0, this.curr_value);
}
if(this.value_elt)
this.value_elt.textContent = String(value);
}
else if(index == 1){
this.scroll_range = value;
this.set_scroll();
}
else if(index == 2){
this.scroll_visible = value;
this.set_scroll();
}
//don't update if draging and setpoint ghost doesn't exist
if(!this.drag || (this.setpoint_elt != undefined)){
this.update_DOM(this.curr_value, this.handle_elt);
}
}
set_scroll(){
//check if range is bigger than visible and set scroll size
if(this.scroll_range > this.scroll_visible){
this.scroll_size = this.scroll_range - this.scroll_visible;
this.range[0] = 0;
this.range[1] = this.scroll_size;
}
else{
this.scroll_size = 1;
this.range[0] = 0;
this.range[1] = 1;
}
}
update_DOM(value, elt){
let [min,max,start,totallength] = this.range;
// check if handle is resizeable
if (this.scroll_size != undefined){ //size changes
//get parameters
let length = Math.max(min,Math.min(max,(Number(value)-min)*max/(max-min)));
let tip = this.range_elt.getPointAtLength(length);
let handle_min = totallength*this.min_size;
let step = 1;
//check if range is bigger than max displayed and recalculate step
if ((totallength/handle_min) < (max-min+1)){
step = (max-min+1)/(totallength/handle_min-1);
}
let kx,ky,offseY,offseX = undefined;
//scale on x or y axes
if (this.fi > 0.75){
//get scale factor
if(step > 1){
ky = handle_min/this.handle_orig.height;
}
else{
ky = (totallength-handle_min*(max-min))/this.handle_orig.height;
}
kx = 1;
//get 0 offset to stay inside range
offseY = start.y - (this.handle_orig.height + this.handle_orig.y) * ky;
offseX = 0;
//get distance from value
tip.y =this.range_elt.getPointAtLength(0).y - length/step *handle_min;
}
else{
//get scale factor
if(step > 1){
kx = handle_min/this.handle_orig.width;
}
else{
kx = (totallength-handle_min*(max-min))/this.handle_orig.width;
}
ky = 1;
//get 0 offset to stay inside range
offseX = start.x - (this.handle_orig.x * kx);
offseY = 0;
//get distance from value
tip.x =this.range_elt.getPointAtLength(0).x + length/step *handle_min;
}
elt.setAttribute('transform',"matrix("+(kx)+" 0 0 "+(ky)+" "+(tip.x-start.x+offseX)+" "+(tip.y-start.y+offseY)+")");
}
else{ //size stays the same
let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
let tip = this.range_elt.getPointAtLength(length);
elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")");
}
// show or hide ghost if exists
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) {
//unbind events
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);
//reset drag flag
if(this.drag){
this.drag = false;
}
// get final position
this.update_position(evt);
}
on_drag(evt){
//ignore drag event for X amount of time and if not selected
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){
var html_dist = 0;
let [min,max,start,totallength] = this.range;
//calculate size of widget in html
var range_borders = this.range_elt.getBoundingClientRect();
var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top];
var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width );
//get range and mouse coordinates
var mouseX = undefined;
var 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 position
if (this.handle_click){ //if clicked on handle
let moveDist = 0, resizeAdd = 0;
let range_percent = 1;
//set paramters for resizeable handle
if (this.scroll_size != undefined){
// add one more object to stay inside range
resizeAdd = 1;
//chack if range is bigger than display option and
// calculate percent of range with out handle
if(((max/(max*this.min_size)) < (max-min+1))){
range_percent = 1-this.min_size;
}
else{
range_percent = 1-(max-max*this.min_size*(max-min))/max;
}
}
//calculate value difference on x or y axis
if(this.fi > 0.7){
moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((this.handle_click[1]-mouseY)/Math.sin(this.fi));
}
else{
moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((mouseX-this.handle_click[0])/Math.cos(this.fi));
}
this.curr_value = Math.ceil(this.handle_click[2] + moveDist);
}
else{ //if clicked on widget
//get handle distance from mouse position
if (minX > mouseX && minY < mouseY){
html_dist = 0;
}
else if (maxX < mouseX && maxY > mouseY){
html_dist = range_length;
}
else{
if(this.fi > 0.7){
html_dist = (minY - mouseY)/Math.sin(this.fi);
}
else{
html_dist = (mouseX - minX)/Math.cos(this.fi);
}
}
//calculate distance
this.curr_value=Math.ceil((html_dist/range_length)*(this.range[1]-this.range[0])+this.range[0]);
}
//check if in range and apply
if (this.curr_value > max){
this.curr_value = max;
}
else if (this.curr_value < min){
this.curr_value = min;
}
this.apply_hmi_value(0, this.curr_value);
//redraw handle
this.request_animate();
}
animate(){
// redraw handle on screen refresh
// check if setpoint(ghost) handle exsist otherwise update main handle
if(this.setpoint_elt != undefined){
this.update_DOM(this.curr_value, this.setpoint_elt);
}
else{
this.update_DOM(this.curr_value, this.handle_elt);
}
}
on_select(evt){
//enable drag flag and timer
this.drag = true;
this.enTimer = true;
//bind events
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);
// check if handle was pressed
if (evt.currentTarget == this.handle_elt){
//get mouse position on the handle
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;
}
//save coordinates and orig value
this.handle_click = [mouseX,mouseY,this.curr_value];
}
else{
// get new handle position and reset if handle was not pressed
this.handle_click = undefined;
this.update_position(evt);
}
//prevent next events
evt.stopPropagation();
}
init() {
//set min max value if not defined
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;
// save initial parameters
this.range_elt.style.strokeMiterlimit="0";
this.range = [min, max, this.range_elt.getPointAtLength(0),this.range_elt.getTotalLength()];
let start = this.range_elt.getPointAtLength(0);
let end = this.range_elt.getPointAtLength(this.range_elt.getTotalLength());
this.fi = Math.atan2(start.y-end.y, end.x-start.x);
this.handle_orig = this.handle_elt.getBBox();
//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);
this.handle_elt.addEventListener("mousedown", this.bound_on_select);
this.element.addEventListener("mousedown", this.bound_on_select);
this.element.addEventListener("touchstart", this.bound_on_select);
//touch recognised as page drag without next command
document.body.addEventListener("touchstart", function(e){}, false);
//save ghost style
if(this.setpoint_elt != undefined){
this.setpoint_style = this.setpoint_elt.getAttribute("style");
this.setpoint_elt.setAttribute("style", "display:none");
}
}
}
||
template "widget[@type='Slider']", mode="widget_defs" {
param "hmi_element";
labels("handle range");
optional_labels("value min max setpoint");
|,
}