Lynel Hudson
Lynel Hudson

Reputation: 2405

Smoother CSS transitions on element

const dot = document.querySelector( `.dot` ).style;

function getRandomInteger( min,max ) {
  min = Math.ceil( min );
  max = Math.floor( max );
  return Math.floor( Math.random() * ( max - min + 1 ) ) + min;
}

// use randomInteger for x and y values 
// for transform: translate( x%,y% )
// range here will be from negative integer 
// to positive integer. The CSS unit is a %
function move( element,range ) { 
  element.transform = 
    `
      translate( 
        ${ getRandomInteger( -range,range ) }%,
        ${ getRandomInteger( -range,range ) }%
      )
    ` 
}

//range here is 250 negative ad positive percent
setInterval( function() { move( dot,250 ) },500 );
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
html, body {
  overflow: hidden;
  height: 100%;
}
body {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #eee;
}
.dot {
  border-style: none;
  border-radius: 50%;
  width: 2.5rem;
  height: 2.5rem;
  background-color: rgba( 0,0,0,0.5 );
  transition-property: transform;
  transition-duration: 2s;
  transition-timing-function: ease-in-out;
}
<hr class='dot'>

This code above moves a dot to a random position on the page.

It works as expected except for the transitions from one location to another. The goal being a smoother transition in between each movement.

The idea initially was to create a floating or hovering effect with subtle movements similar to an object gently floating on the surface of water.

We used transition-timing-function: ease-in-out on the last CSS line as an attempt to lessen the abruptness of the direction changes above. Yet altering the timing function to any value doesn't seem to help very much. Including custom cubic-bezier values.

How can we get the animation to change directions less abruptly and be a smoother overall motion?

Upvotes: 2

Views: 1041

Answers (1)

UserTest013
UserTest013

Reputation: 563

You can create a list with keyframes by javascript and creates a new animation based on the list. In this way you will avoid setInterval and the browser will animate your element by himself.

If you want even more smoother movement you should use less random values and provide keyframe list with smother positions.

function getRandomInteger(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

const range = 200;
const steps = 50;

const keyframes = Array.apply(null, Array(50)).map(() => {
  return {
    transform: `translate(${getRandomInteger(-range, range)}%, ${getRandomInteger(-range, range)}%)`
  };
});

document.querySelector('.dot').animate(keyframes, {
  duration: steps * 500, // 0.5 sec for keyframe
  direction: 'alternate',
  fill: 'both',
  iterations: Infinity
});
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html,
body {
  overflow: hidden;
  height: 100%;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #eee;
}

.dot {
  border-style: none;
  border-radius: 50%;
  width: 2.5rem;
  height: 2.5rem;
  background-color: rgba( 0, 0, 0, 0.5);
}
<hr class='dot'>

If you would like to achieve really smooth movement you should remove randomize and use some mathematical function to define path. Here is example based on Lissajous curve but you can use any other curves.

function getLissajousCurve(radius, steps, a, b, d, A, B) {
  const result = [];
  for (let t = 0; t <= 2 * Math.PI; t += Math.PI / steps) {
    const x = radius * A * Math.sin(a * t + d);
    const y = radius * B * Math.sin(b * t);
    result.push([x, y]);
  }
  return result;
}

const steps = 50;

const curve = getLissajousCurve(200, steps, 4, 5, 0, 1, 1);

const keyframes = curve.map(([x, y]) => {
  return {
    transform: `translate(${x}%, ${y}%)`
  };
});

document.querySelector('.dot').animate(keyframes, {
  duration: steps * 500, // 0.5 sec for keyframe
  direction: 'alternate',
  fill: 'both',
  iterations: Infinity
});
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html,
body {
  overflow: hidden;
  height: 100%;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #eee;
}

.dot {
  border-style: none;
  border-radius: 50%;
  width: 2.5rem;
  height: 2.5rem;
  background-color: rgba( 0, 0, 0, 0.5);
}
<hr class='dot'>

Upvotes: 3

Related Questions