0

I'm trying to make a slider in javascript with canvas. The moveSlider() function is called by 'pointermove' eventlistener, and it changes the X coordinate of a rectangle stored in 'Slider' object in 200 pixel range.

I want it to be done in 11 steps, and cant seems to figure out how.

Also for some reason, if I add value of 'delta' to Slider.x the slider moves the opposite direction of the mouse, why?

any help is appreciated.

var startPos = e.clientX

function moveSlider(e) {

    let x = ctx.canvas.width / 2
    let delta = startPos - e.clientX;
    startPos = e.clientX;

    Slider.x -= delta;
    Slider.x = Slider.x > x+100 ? x+100 : Slider.x;  
    Slider.x = Slider.x < x-100 ? x-100 : Slider.x; 
}
2
  • It looks as if you're subtracting the value of delta: Slider.x -= delta; try adding it: Slider.x += delta; Commented Mar 17 at 0:40
  • Because this subtraction is backwards: let delta = startPos - e.clientX Commented Mar 17 at 2:15

2 Answers 2

1

You can try setting up an array that contains the "snap to" X-values along the slider. You will also need a function that accepts an X value, and returns the closest "snap to" value. Something like this:

const left = 100;
const width = 200;
const numStops = 11;

const lerp = (from, to, pct) => (to - from) * pct + from;

const sliderPositions = Array.from(
  {length: numStops}, 
  (_, i) => lerp(left, left + width, i / (numStops - 1))
);

const getClosestSliderPosition = xPos => 
  sliderPositions.reduce((acc, el) => {
    if (Math.abs(xPos - el) < Math.abs(xPos - acc))
      acc = el;
    return acc;
  });

console.log(getClosestSliderPosition(150));
console.log(getClosestSliderPosition(115));
console.log(sliderPositions);

Then in your event handler you can use the getClosestSliderPosition function like so:

function moveSlider(e) {
    Slider.x = getClosestSliderPosition(e.clientX);
}
Sign up to request clarification or add additional context in comments.

3 Comments

This is cool, one unintended side effect is the size of the slider handle is 100 pixel wide. So, grabbing a slider, and moving it again makes it jump to a new position, based on where within that 100 pixel it was grabbed.
In that case you would need to use the x-position of the center of the slider handle instead of clientX.
nvm, i figured it out, The clientX needs to be offseted by its position relative to slider.x
0

Canvas is fixed in px as you are already aware. If you want to streamline your code why not base it on:

  1. e.pageX: The horizontal coordinate of your mouse in relation to the left edge of the document.

  2. The offset between the left edge of the document and the left edge of the area within the canvas (not the offset of the whole canvas).

  3. The offset between the left edge of the document and the right edge of the area within the canvas.

In the example below, the area within the canvas is the red line which starts 60px from the left edge of the document and its right edge is 260px from the left edge of the document. The essential lines of the "pointermove" event handler are:

if (dragging && e.pageX < 260 && e.pageX > 59) {
  x = e.pageX;
  draw();

const cvs = document.getElementById("cvs");
const ctx = cvs.getContext("2d");
const xpx = document.getElementById("xpx");
const xpc = document.getElementById("xpc");

const w = 220;
const h = 30;

let dragging = false;
let x = 60;
let rAF;

const rect = (x, y, w, h) => {
  ctx.beginPath();
  ctx.rect(x, y, w, h);
  ctx.closePath();
  ctx.fill();
}

const clear = () => {
  ctx.clearRect(50, 50, w, h);
}

const draw = () => {
  clear();
  ctx.fillStyle = "#CCCCCC";
  rect(50, 50, w, h);
  ctx.fillStyle = "#FF0000";
  rect(60, 60, 200, 10);
  ctx.fillStyle = "#444444";
  rect(x - 5, 60, 10, 10);
  if (dragging) {
    rAF = requestAnimationFrame(draw);
  }
  cancelAnimationFrame(rAF);
};

const pointerMove = (e) => {
  if (dragging && e.pageX < 260 && e.pageX > 59) {
    x = e.pageX;
    draw();
    cvs.style.cursor = "ew-resize";
    xpx.value = Math.ceil(x) + "px";
    xpc.value = Math.ceil(((x - 60) / 200) * 100) + "%";
  }
};

const pointerDown = (e) => {
  if (e.pageX < 260 && e.pageX > 59) {
    x = e.pageX;
    dragging = true;
    cvs.onpointermove = pointerMove;
    cvs.style.cursor = "ew-resize";
    xpx.value = Math.ceil(x) + "px";
    xpc.value = Math.ceil(((x - 60) / 200) * 100) + "%";
  }
};

const pointerClick = (e) => {
  if (e.pageX < 260 && e.pageX > 59) {
    x = e.pageX;
  }
};

const pointerExit = () => {
  dragging = false;
  cvs.onpointermove = null;
  cvs.style.cursor = "default";
};

cvs.onpointerdown = pointerDown;
cvs.onclick = pointerClick;
cvs.onpointerup = pointerExit;
draw();
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box
}
<canvas id="cvs" width="275" height="90"></canvas>
<output id="xpx"></output>
<output id="xpc"></output>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.