/*
* Created by yvesb on 08/10/2016.
*/
/*
* MathGraph32 Javascript : Software for animating online dynamic mathematics figures
* https://www.mathgraph32.org/
* @Author Yves Biton (yves.biton@sesamath.net)
* @License: GNU AGPLv3 https://www.gnu.org/licenses/agpl-3.0.html
*/
import { cens, getStr, preventDefault } from '../kernel/kernel'
import { mousePosition, touchPosition } from '../kernel/kernelAdd'
import constantes from '../kernel/constantes'
import $ from 'jquery'
export default Slider
/**
*
* @param {MtgApp} app
* @param width
* @param height
* @param min
* @param max
* @param startval
* @param digits
* @param step
* @param tip
* @param transform
* @param {string} append
* @param y
* @param fonc
* @constructor
*/
function Slider (app, width, height, min, max, startval, digits, step, tip, transform, append, y, fonc) {
this.target = 'right' // Pour les tips
const zf = app.zoomFactor
width *= zf
height *= zf
this.app = app
this.width = width
this.height = height
this.min = min
this.max = max
this.val = startval
this.fonc = fonc
this.step = step
this.tip = getStr(tip)
this.append = append // L'éventuelle chaîne à rajouter au bout de l'affichage
this.y = y // Pour la ligne d'affichage du tip
this.captured = false
const gap = constantes.sliderGap * zf
const g = cens('g', {
transform
})
this.container = g
// Un rectangle à gauche qui contiendra le curseur et réagira à la souris
const widthc = width - digits * constantes.sliderDigitsFontSize * 0.6 * zf // Largeur du curseur
// this.widthc = widthc;
const rectg = cens('rect', {
x: 0,
y: 0,
width: widthc,
height,
stroke: 'none',
fill: constantes.buttonBackGroundColor
})
g.appendChild(rectg)
const rectd = cens('rect', {
x: widthc,
y: 0,
width: width - widthc,
height,
fill: constantes.buttonBackGroundColor
})
g.appendChild(rectd)
const yl = (height - constantes.sliderThickness * zf) / 2
// On crée le trait horizontal
this.segmentWidth = widthc - 2 * constantes.sliderRadius * zf - 2 * gap
this.xc = gap + constantes.sliderRadius * zf // Abscisse de début du segment
const seg = cens('line', {
stroke: constantes.sliderColor,
'stroke-linecap': 'round',
x1: this.xc,
y1: yl,
x2: gap + this.segmentWidth,
y2: yl
})
g.appendChild(seg)
/*
seg.addEventListener("mousedown", function(evt) {
self.onbuttonDeviceDown(evt);
});
*/
$(seg).css('pointer-events', 'none')
// On crée le rond qui sera le curseur
this.xCurseur = gap + this.abscisse(startval)
this.circ = cens('ellipse', {
cx: this.xCurseur,
cy: yl,
rx: constantes.sliderRadius * zf,
ry: (constantes.sliderHeight / 2 - 2) * zf,
stroke: 'black',
fill: 'url(#sliderGrad)'
})
$(this.circ).css('pointer-events', 'none')
g.appendChild(this.circ)
// même si c'est les params par défaut, faut le préciser explicitement pour que chrome arrête de râler en console
const activeOpts = { capture: false, passive: false }
const upListener = this.ondeviceup.bind(this)
rectg.addEventListener('mouseup', upListener, activeOpts)
rectg.addEventListener('touchend', upListener, activeOpts)
rectg.addEventListener('mousedown', this.onDeviceDown.bind(this, mousePosition), activeOpts)
rectg.addEventListener('touchstart', this.onDeviceDown.bind(this, touchPosition), activeOpts)
rectg.addEventListener('mousemove', this.onDeviceMove.bind(this, mousePosition), activeOpts)
rectg.addEventListener('touchmove', this.onDeviceMove.bind(this, touchPosition), activeOpts)
// On crée à droite un affichage de la valeur
this.txt = cens('text', {
x: String(width - 2),
y: height - 4 * zf,
'font-size': String(constantes.sliderDigitsFontSize * zf) + 'px',
style: 'stroke:blue;font-size:' + String(constantes.sliderDigitsFontSize * zf) + 'px;' + 'font-family:serif;text-anchor:end;'
})
this.txt.appendChild(document.createTextNode(this.val + this.append))
g.appendChild(this.txt)
}
Slider.prototype.abscisse = function (val) {
return (val - this.min) / (this.max - this.min) * this.segmentWidth + constantes.sliderRadius * this.app.zoomFactor
}
Slider.prototype.ondeviceup = function (evt) {
this.captured = false
preventDefault(evt)
// evt.stopPropagation(evt);
}
// fonc est imposé par le bind, ev filé par l'EventListener
Slider.prototype.onDeviceDown = function (fonc, ev) {
let newval
const zf = this.app.zoomFactor
// this.captured = false;
const svg = this.app.svgFigure
// Ligne suivante modifié version 6.5.1
const point = fonc(svg, ev, this.app.zoomFactor) // Renvoie les coordonnées de la souris par rapport au svgFigure
const x = point.x - parseInt(svg.getAttribute('width'))
if (Math.abs(x - this.xCurseur) < 2 * constantes.sliderRadius * zf) this.captured = true
else {
if (x > this.xCurseur) {
newval = this.val + this.step
if (newval > this.max) newval = this.val
this.updateValue(newval)
this.updatePosition()
} else {
if (x < this.xCurseur) {
newval = this.val - this.step
if (newval < this.min) newval = this.val
this.updateValue(newval)
this.updatePosition()
}
}
}
preventDefault(ev)
// ev.stopPropagation();
}
/**
* Listener du move, actif (il faut préciser passive: false au addEventListener)
* @param {function} fonc la fonction imposée par le .bind()
* @param {MouseEvent|TouchEvent} ev l'événement transmis par l'EventListener
*/
Slider.prototype.onDeviceMove = function (fonc, ev) {
const self = this
if (this.captured) {
const svg = this.app.svgFigure
// Ligne suivante modifiée version 6.5.1
const point = fonc(svg, ev, this.app.zoomFactor) // Renvoie les coordonnées de la souris par rapport au svgFigure
let x = point.x - parseInt(svg.getAttribute('width'))
const x1 = this.xc + this.segmentWidth
if (x < this.xc) x = this.xc
else if (x > x1) x = x1
const abs = (x - this.xc) / this.segmentWidth
// Modifié version 7.3 pour optimisation
// const newval = Math.floor(this.min + abs * (this.max - this.min) + 0.5)
const newval = Math.round(this.min + abs * (this.max - this.min))
this.updateValue(newval)
this.updatePosition()
preventDefault(ev)
// ev.stopPropagation();
} else {
if (!self.tipDisplayed) {
self.app.cacheTip() // Sans argument pour effacer l'ancien tip quel qu'il soit
self.tipDisplayed = false // Sera modifié par app
self.app.setTip(self)
setTimeout(function () {
self.app.cacheTip(self)
}, 2500)
}
}
}
Slider.prototype.updatePosition = function () {
this.xCurseur = this.xc + this.segmentWidth * (this.val - this.min) / (this.max - this.min)
this.circ.setAttribute('cx', this.xCurseur)
this.updateDisplay()
}
Slider.prototype.updateDisplay = function () {
this.txt.removeChild(this.txt.firstChild)
this.txt.appendChild(document.createTextNode(this.val + this.append))
}
Slider.prototype.updateValue = function (newval) {
this.val = newval
this.fonc(newval) // Pour appeler la fonction affectant cette valeur
}
Slider.prototype.update = function (newVal) {
this.updateValue(newVal)
this.updatePosition()
}