The sinuous waveform in JavaScript

track : function(x, y, top, ampl) {
        return {
            top : top + 2,
            x   : x + ampl * Math.sin(top / 20),
            y   : (top / this.screenHeight < 0.65) ? y + 2 : 1 + y + ampl * Math.cos(top / 25)
        };
    }

This specific function is responsible for controlling the movement of snowflakes in a sinusoidal pattern.

But how exactly does it accomplish this task? Let's dive deeper into its workings:

The function utilizes Math.sin for determining the horizontal position (x) and uses Math.cos for calculating the vertical position (y). It may seem counterintuitive to some, as typically these trigonometric functions are used in reverse order in similar contexts. The reason behind using top/20 and top/25 specifically needs further clarification.

Here is the entire code snippet for reference:

<script type="text/javascript">
var snowflakes = { // Namespace
    /* Settings */

    pics : [

        ['snow.gif' , 24, 24],
        ['snow2.gif', 24, 24],
        ['snow3.gif', 24, 24]
    ],

    track : function(x, y, top, ampl) {
        return {
            top : top + 2,
            x   : x + ampl * Math.sin(top / 20),
            y   : (top / this.screenHeight < 0.65) ? y + 2 : 1 + y + ampl * Math.cos(top / 25)
        };
    },

    quantity : 30,

    minSpeed : 20, // 1 - 100, minSpeed <= maxSpeed

    maxSpeed : 40, // 1 - 100, maxSpeed >= minSpeed

    isMelt : true, // true OR false
    /* Properties */
    screenWidth : 0,
    screenHeight : 0,
    archive : [],
    timer : null,
    /* Methods */
    addHandler : function(object, event, handler, useCapture) {
        if (object.addEventListener) object.addEventListener(event, handler, useCapture);
        else if (object.attachEvent)object.attachEvent('on' + event, handler);
        else object['on' + event] = handler;
    },
    create : function(o, index) {
        var rand = Math.random();
        this.timer = null;
        this.o = o;
        this.index = index;
        this.ampl = 3 + 7*rand;
        this.type =  Math.round((o.pics.length - 1) * rand);
        this.width = o.pics[this.type][1];
        this.height = o.pics[this.type][2];
        this.speed = o.minSpeed + (o.maxSpeed - o.minSpeed) * rand;
        this.speed = 1000 / this.speed;
        this.deviation = o.maxDeviation * rand;
        this.x = o.screenWidth * rand - this.width;
        this.y = 0 - this.height;
        this.top = this.y;
        this.img = document.createElement('img');
        this.img.src = o.pics[this.type][0];
        this.img.style.top = this.y + 'px';
        this.img.style.position = 'absolute';
        this.img.style.zIndex = 10000;
        this.img.style.left = this.x + 'px';
        this.img.obj = this;
        if (o.isMelt) this.img.onmouseover = function() {
            clearTimeout(this.obj.timer);
            this.obj.timer = null;
            this.parentNode.removeChild(this);
        }
        document.body.appendChild(this.img);
        this.move();
    },
    init : function() {
        this.screenWidth = window.innerWidth ? window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.offsetWidth);
        this.screenWidth = navigator.userAgent.toLowerCase().indexOf('gecko') == -1 ? this.screenWidth : document.body.offsetWidth;
        this.screenHeight = window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.offsetHeight);
        this.screenScroll = (window.scrollY) ? window.scrollY : document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;
        this.archive[this.archive.length] = new this.create(this, this.archive.length);
        clearTimeout(this.timer);
        this.timer = null
        this.timer = setTimeout(function(){snowflakes.init()}, 60000 / this.quantity);
    }
};
snowflakes.create.prototype = {
    move : function() {
        var newXY = this.o.track(this.x, this.y, this.top, this.ampl);
        this.x   = newXY.x;
        this.y   = newXY.y;
        this.top = newXY.top;
        if (this.y < this.o.screenHeight + this.o.screenScroll - this.height) {
            this.img.style.top  = this.y + 'px';
            this.x = this.x < this.o.screenWidth - this.width ? this.x : this.o.screenWidth - this.width;
            this.img.style.left = this.x + 'px';
            var index = this.index;
            this.timer = setTimeout(function(){snowflakes.archive[index].move()}, this.speed);
        } else {
            delete(this.o.archive[this.index]);
            this.img.parentNode.removeChild(this.img);
        }
    }
};
snowflakes.addHandler(window, 'load', function() {snowflakes.init();});
snowflakes.addHandler(window, 'resize', function() {snowflakes.init();});
    </script>

Answer №1

The fundamental sine function is defined as:

f(x) = A sin(wt + p)

where

  • A represents the amplitude
  • w signifies the frequency
  • p denotes the phase

These variables dictate the appearance of the graph of f.

The amplitude serves as a scaling factor, with larger A leading to greater (absolute values) peaks and lows of f.

The frequency determines how quickly the sine function cycles through all its values before starting anew - as sine is a periodic function. A higher k results in a quicker completion of one period by f.

p represents the phase, essentially determining the initial position of the function, either shifted to the right (positive p) or left (negative). For a visual representation, refer here for graphs.

Your example provides a generalized version of

f: R->R², f(t)=(sin(t), cos(t))

This serves as one of the parametrizations of the unit circle. When incrementing t monotonically and plotting x (sin(t)) and y (cos(t)), a point travels along a circle with radius 1.

Your generalized function stands as

f: R->R², f(t) = (A sin(1/wt), A cos(1/wt)), w > 1

In your scenario, A=ampl, t=top, w=20 for the x coordinate, and w=25 for the y coordinate. These slight variations in w introduce a jittery movement, turning the perfect circle into a somewhat "distorted" ellipse - snowflakes indeed do not descend in precise circles. This also gives the flake's path a seemingly more random appearance than straightforward circular trajectories. It's important to note that this is an illusion, as the motion remains deterministic and periodic - it's just that the x and y movements are "out of sync," lengthening the time needed to complete one cycle.

w is selected as > 1 to decelerate the circular motion. Opting for a larger w will result in a reduced frequency, causing the moving point to traverse a full circle much slower.

The choice of a larger A yields an expanded circle diameter.

Answer №2

Expanding the sine wave amplifies the curves for better visibility.

Check out this demonstration I created on a coding platform. When I modify the values to 1, the motion becomes less captivating. http://jsfiddle.net/AbM9z/1/

Understanding the input values of the function would provide more context.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Animating several elements by toggling their classes

I'm struggling to implement smooth animations on this box. I've tried using 'switchClass' but can't seem to keep everything together. Any help would be greatly appreciated. Here is the code snippet for reference: <script src=" ...

Adjust the sizes of the points according to the level of zoom

I've been working on creating a dynamic map in d3.js that displays US Science funding agencies as points, with their sizes scaling based on zoom level. I referred to this starter kit for guidance. While there are other solutions out there, they often ...

Is it possible to animate multiple SVGs on a webpage with just the click of a button?

Is there a way to trigger the animation in the SVG each time a next/prev button is clicked while navigating through a carousel where the same SVG is repeated multiple times? The carousel is built using PHP and a while loop. jQuery(document).ready(function ...

How the logo's placement shifts while zooming out (using CSS and Angular 4+)

I am facing an issue with my navbar that includes a logo (MostafaOmar) which shifts position when zoomed out. If you try zooming to 70%, you will notice the logo's position changes as well. Is there a way to make it stay in place at 100% zoom? Here ...

Save the retrieved data in a variable and pass the entire JSON object as a prop to a React component

Having some trouble with my code. I need the app to fetch a JSON from an API and pass it as a prop so that each component file can use it and display the element on the screen. The issue is, I can't seem to figure out how to store the fetched informa ...

What is the best way to display a placeholder instead of the state's value when a page loads?

I'm having trouble displaying the placeholder of an input instead of its state value. When the page loads, it shows an empty select box because the testType state is null initially. How can I make sure that only the placeholder is shown instead of the ...

What steps should I take to troubleshoot and resolve the connection issue that arises while trying to execute npm install

Following the guidelines from: https://www.electronjs.org/docs/tutorial/first-app I executed commands like mkdir, cd, and npm init. They all ran successfully, generating a file named package.json. Subsequently, I entered npm install --save-dev electron w ...

req.body is not defined or contains no data

I am facing an issue with my controllers and routers. bookController.js is functioning perfectly, but when I try to use userControllers for registration and login logic, req.body always appears empty. I tried logging the form data using console.log, but it ...

Mastering the Art of Resizing Video Controls: A

https://i.stack.imgur.com/jTgap.png https://i.stack.imgur.com/Exf6Y.png On the initial screenshot, the video player displays normal controls. However, on the same website with identical CSS, the second video player shows different control settings. // C ...

Is it the browser's responsibility to convert ES6 to ES5 internally?

Given the support for ES6 in modern browsers, do they internally convert ES6 to ES5 before executing the code? Or can they process ES6 natively using a C++ engine? If they are able to run ES6 directly, how do they guarantee that executing ES6 code produce ...

Would this be considered a practical method for showcasing an image with jQuery?

Is there a more efficient way to display an image once it has been loaded using this code? LightBoxNamespace.SetupLightBox = function(path, lightBox) { // Create a new image. var image = new Image(); // The onload function must come before ...

What is the best way to integrate AJAX with draggable columns in a Laravel application?

I have successfully integrated draggable functionality for table columns using jQuery Sortable within my Laravel application. I am now facing the challenge of updating the database with the data from these columns through AJAX. Despite researching online ...

The order of event handlers in jQuery

I am currently setting up event binding for a text element dynamically. Take a look at the following code snippet: <input type="text" id="myTxt" /> <script type="text/javascript"> function attachEvent1(element){ element.keyup(func ...

accurate JSONP reply

Currently, I am experimenting with JSONP locally to receive a correct response and pass it into my callback function jsonp_callback. I am referencing code from: How do I set up JSONP? header('content-type: application/json; charset=utf-8'); $dat ...

Set iframe or form to be in a fixed position

Having issues with my site where an iframe is contained within a div. When only one character is entered in the telephone box, validation fails and red warning text is displayed. Furthermore, after clicking in the email box and pressing tab twice, the fo ...

Next.js optimizes the page loading process by preloading every function on the page as soon as it loads, rather than waiting for them to

My functions are all loading onload three times instead of when they should be called. The most frustrating issue is with an onClick event of a button, where it is supposed to open a new page but instead opens multiple new pages in a loop. This creates a c ...

The command "json_encode($array)" does not seem to be encoding the complete array

I ran a test on the following: <? echo json_encode($array) ?> result: ["PG","Kevin Sad","8000","12"] Upon placing it within a form option value to be selected by my script function: <option value=<? echo json_encode($array) ?> > op ...

The error encountered is an unhandled rejection with a message stating "TypeError: Cannot access property 'username' of null

My tech stack includes NodeJS, PassportJS, MySQL, and Sequelize (ORM for MySQL). The following code snippet is taken from my Passport.JS file. Whenever a user registers on my website, an error is returned if the username or email is already in use. If both ...

Encountering an undefined response in API call using Fetch with Express Nuxt

I've hit a roadblock with a task involving Express, API, and Fetch. I'm utilizing Nuxt along with Shopify API endpoints to retrieve data such as orders. Below is my express API Endpoint which should return an array of objects (orders). const bod ...

Segment will expand completely upon inserting a new division

Lately, I've been trying to simplify things by consolidating my JavaScript functions into one file instead of using multiple separate files. The idea was to merge all the functions together and wrap each one in its own function so that they can be eas ...