Time-Based Animation

by maxell4o

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400&display=swap" rel="stylesheet">

<div class="box"><i class="far fa-hand-pointer"></i></div>

SCSS

body {
    margin: 0;
	font-family: 'Open Sans', sans-serif;
}

.box {
    position: absolute;
    width: 77px;
    height: 77px;
    background: rgba(50, 150, 50, 0.5);
    top: 10px;
    text-align: center;
    line-height: 77px;
    font-size: 30px;
	font-weight: 300;
	transition: border-radius 0.3s ease, opacity 0.3s ease;
	border-radius: 2px;
	color: #fff;
	cursor: pointer;
	
	&[direction="left"]{
		border-radius: 50%;
		background: rgba(50, 120, 150, 0.5);
		
		&:after{
			right: auto;
			left: 50%;
			transform: translateX(-50%);
		}
	}
	
    &:after {
        content: 'fps';
        font-size: 13px;
        color: #fff;
        position: absolute;
        bottom: 3px;
        right: 3px;
        line-height: initial;
		transition: all 0.3s ease;
    }
	
	&:hover{
		opacity: 0.7;
	}
}

JavaScript 1.7

class Animation {
    constructor() {
        this.running = false;
        this.frameLoop = null;
        this.now = null;
        this.then = null;
        this.delta = 0;
        this.timer = 0;

        this.$box = {
            element: document.querySelector('.box'),
            speed: 150,
            left: 0
        }

        this.Events();
    }

    Events() {
        this.$box.element.addEventListener('click', () => {
            window.cancelAnimationFrame(this.frameLoop);
            if (!this.running) {
                this.then = Date.now();
                this.Frame();
                this.running = true;
                return;
            }
            this.running = false;
        });
    }

    Frame() {
        this.now = Date.now();
        this.delta = (this.now - this.then) / 1000;
        this.then = Date.now();
        this.Render();
        this.frameLoop = window.requestAnimationFrame(this.Frame.bind(this));
    }

    Render() {
        // Update FPS on every 1/4s
        if (this.timer > 0.05) {
            this.timer = 0;
            this.$box.element.innerHTML = (1 / this.delta) | 0;
        }
        this.timer += this.delta;

        // Actual elements animation
        this.$box.direction = this.$box.direction || 'right';
        if (this.$box.direction == 'right') {
            this.$box.left = (this.$box.left || 0) + this.$box.speed * this.delta;
            if (window.innerWidth <= this.$box.element.clientWidth + this.$box.left) {
                this.$box.direction = 'left';
            }
        }
        if (this.$box.direction == 'left') {
            this.$box.left = (this.$box.left || 0) - this.$box.speed * this.delta;
            if (this.$box.left <= 0) {
                this.$box.direction = 'right';
            }
        }
        this.$box.element.style.left = (this.$box.left + 0.5 | 0) + 'px';
        this.$box.element.setAttribute('direction', this.$box.direction);
    }
}
new Animation();