0

I am using the following translateRange function to calculate the range between multiple factors like this:

function translateRange(
    input,
    inputMin,
    inputMax,
    outputMin,
    outputMax
) {
    let inputMinA = Math.min(inputMin, input);
    let inputMaxA = Math.max(inputMax, input);

    return (
        outputMin +
        ((outputMax - outputMin) * (input - inputMinA)) /
            (inputMaxA - inputMinA)
    );
};


let opacity = translateRange(
  scrollY, //input
  0, //animation start
  100, //animation end
  0, // from 0
  1 // to
);

This works best, but i want to pass multiple values to the function. For e.g. i want to pass xand y values for transform animations at the same time. How can i change the function to get this working? I would like to end with something like this:

let translate3d = translateRange(
   scrollY,
   0,
   100,
   [-10, -5], // x values (start and end)
   [-4, 1] // y values (start and end)
);

When you scroll, i want to get the calculated values like this:

-10, -4 // x and y start values
Niklas Grewe
  • 153
  • 1
  • 10
  • So isn't there a `scrollX` argument missing in your final example? And have you tried to loop over the arrays? Why didn't this work? – trincot Oct 08 '20 at 05:47

2 Answers2

1

Why not reuse your function like this?

function translateRange(
    input,
    inputMin,
    inputMax,
    outputMin,
    outputMax
) {
    let inputMinA = Math.min(inputMin, input);
    let inputMaxA = Math.max(inputMax, input);

    return (
        outputMin +
        ((outputMax - outputMin) * (input - inputMinA)) /
            (inputMaxA - inputMinA)
    );
};

const translateArray = (input, inputMin, inputMax, outputMin, outputMax) => 
    outputMax.map((_, i) => translateRange(input[i] ?? input, inputMin[i] ?? inputMin, inputMax[i] ?? inputMax, outputMin[i] ?? outputMin, outputMax[i] ?? outputMax));

const translate3d_1 = translateArray([0, 50], [0, 0], [100, 100], [-10, -5], [-4, 1]);

const translate3d_2 = translateArray(50, 0, 100, [-10, -5], [-4, 1]);

console.log(translate3d_1);
console.log(translate3d_2);
Hao Wu
  • 17,573
  • 6
  • 28
  • 60
  • thanks for your answer. Cool idea. But why i get `-10, -2` on console and not `-10, -4` ? – Niklas Grewe Oct 08 '20 at 06:01
  • @NiklasGrewe The first input is the scroll: `[0, 50]`, to the range `[0, 0] ~ [100, 100]` means it's 0 of the x-axis and half of the y-axis. According to the result map array `[-10, -5] ~ [-4, 1]`, half of the y-axis is `-2`. If the first input is `[0, 0]`, the result will be the starting value which is `[-10, -4]`. If that makes sense. – Hao Wu Oct 08 '20 at 06:04
  • @HaoWu The way I read the question, the `input` , `inputMin` and `inputMax` need to stay one single value that works on all the ranges. Therefore `outputMax.map((_, i) => translateRange(input, inputMin, inputMax, outputMin[i], outputMax[i])` would do the trick – JasperZelf Oct 08 '20 at 06:09
  • @JasperZelf You're right, I changed the answer so all the inputs can be either single values or arrays. But your answer is cleaner, they both would work. – Hao Wu Oct 08 '20 at 06:13
1

I would not recommend doing thin in one single function. It gets quite messy keeping track of all the values.

A cleaner solution would be to calculate it separately for each input as done here:
https://jsfiddle.net/er0zyb4w/

If you want a single function that takes all the inputs: here you go:

(function() {
    document.onmousemove = handleMouseMove;
})();

//https://stackoverflow.com/questions/7790725/javascript-track-mouse-position
function handleMouseMove(event) {
  var eventDoc, doc, body;
  event = event || window.event;
  if (event.pageX == null && event.clientX != null) {
    eventDoc = (event.target && event.target.ownerDocument) || document;
    doc = eventDoc.documentElement;
    body = eventDoc.body;

    event.pageX = event.clientX +
      (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
      (doc && doc.clientLeft || body && body.clientLeft || 0);
    event.pageY = event.clientY +
      (doc && doc.scrollTop  || body && body.scrollTop  || 0) -
      (doc && doc.clientTop  || body && body.clientTop  || 0 );
  }

  // Use event.pageX / event.pageY here
  [opacity, rotation] = translateRange(
    [event.pageX, event.pageY], //input
    [0,0], //animation start
    [500, 200], //animation end
    [1, 0], // from 
    [0, 90] // to
  );
  
  const el = document.getElementById("test");
  el.style.opacity = opacity;
  el.style.transform = `rotate(${rotation}deg)`;
}

function translateRange(
    inputs, // array of input values, x and y for example
    inputsMin, // array of animation start values
    inputsMax, // array of animation stop values
    outputsMin, // array of output min values
    outputsMax // array of output max values
) {
    return inputs.map((_, i)=>{
    let inputsMinA = Math.min(inputsMin[i], inputs[i]);
    let inputsMaxA = Math.max(inputsMax[i], inputs[i]);

    return (
        outputsMin[i] +
        ((outputsMax[i] - outputsMin[i]) * (inputs[i] - inputsMinA)) /
            (inputsMaxA - inputsMinA)
    );
  });
    
};
#test{
  position: fixed;
  top: 100px;
  left: 100px;
  width: 100px;
  height: 100px;
  background: red;
}
<div id="test"></div>
Move your mouse. <br/>
X - axis should change opacity 0 to 500px ==> opacity 1 to 0<br/> 
Y - axis should change rotation 0 to 200px ==> rotation 0 to 90 degrees
JasperZelf
  • 2,731
  • 1
  • 22
  • 34
  • awesome stuff, thank you so much. That's exactly would i am looking for. I would like to change one more thing. Is it possible to change the function so that I can use it in the end like this: `animation({input: [event.pageX, event.pageY], start: [0,0], end: [500, 200], opacity: [1, 0], rotation: [0, 90]})` this way you could make the animation even more dynamic. Do you have an idea how to change that? – Niklas Grewe Oct 08 '20 at 08:35
  • you could. However, the arguments you put into your function become less and less readable. It will become very unclear to see if opacity is linked to X or Y for example. Also, now you could input arrays of 5 variables and you get arrays of 5 outputs back. If you group in this case opacity and rotation start and end values, the amount of arguments would change, making it even more unreadable. – JasperZelf Oct 08 '20 at 08:43
  • ok thanks, I see what you mean. But how can I make the function so dynamic that I don't always have to write this myself: `[opacity, rotation] = translateRange` `el.style.opacity = opacity;...`? Could you show me a little example, so i can get an idea, how I have to change that? most of the time I will only animate one scroll direction. So this should still be clear. – Niklas Grewe Oct 08 '20 at 09:30