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

Capture any clicks that fall outside of the specified set

I am facing an issue with my navigation drop down menu. Currently, the pure CSS functionality requires users to click the link again to close the sub-menu. What I want is for the sub-menu to close whenever a click occurs outside of it. I attempted a solu ...

The AngularJS page on my website is currently stuck in the mobile version, and strangely, the Google Chrome console is

When browsing our AngularJS website in mobile view on Google Chrome, it gets stuck and becomes unresponsive. The developer console does not show any error messages during this time. To replicate the issue, switch to mobile view, click on the "All top compa ...

Using form submission to implement reCAPTCHA v3 in g-recaptcha

Is the Recaptcha API causing trouble? I have the key on both the submit button and the form tag, but it's only sending the sitekey without generating tokens. Any suggestions are welcome. You can either use the key in the form tag: <form method=&qu ...

Tips for adding one document to two separate collections using JavaScript and MongoJS

Having trouble inserting data into two separate tables using javascript and mongojs. While I was able to successfully insert into a single collection by following the npm mongojs documentation, I couldn't find a solution for multiple collections on th ...

Utilizing Twitter API authentication to prevent hitting rate limits

Currently, I am implementing the Twitter API to showcase the most recent tweets from 4 distinct users on my webpage. It seems that once a certain number of calls are made, an error message appears stating "NetworkError: 400 Bad Request" and prevents the tw ...

Bootstrap: Retrieve an image from a modal

I am working on a modal that contains a variety of selectable images. When an image is clicked, I want to change the text of the button in the modal to display the name of the selected image. Additionally, I would like to grab the selected image and displa ...

Unable to display the popup modal dialog on my Rails application

I currently have two models, Post and Comment. A Post has many Comments and a Comment belongs to a Post. Everything is functioning properly with the creation of posts and comments for those posts. Now, I have a new requirement where when clicking on "crea ...

Tips for implementing Papa Parse to parse CSV files using JavaScript

I've been exploring their API without much luck. My goal is to extract data from CSV files that are sent to the client upon server entry. Here's the code snippet I attempted: // Attempting to parse local CSV file Papa.parse("data/premier leagu ...

Can you provide a succinct explanation of what constitutes a well-defined "landing page"?

Could someone provide a clear and concise explanation of what exactly constitutes a landing page and how it should be utilized? I'm struggling to grasp the concept. Where is the optimal placement for a landing page on a website? Is it best placed o ...

Having difficulty accessing data in a JavaScript file within Odoo 9 platform

I have attempted to extract HTML content from JavaScript declared in my module. However, when I try to retrieve content by class name, all I can access is the header contents and not the kanban view. openerp.my_module = function(instance) { var heade ...

Node.js environment variable returns a value of 'undefined'

One method I use to safeguard my bot tokens is by utilizing dotenv to keep them separate from the main app. However, a problem arises when running the code - the environment variables appear as undefined, resulting in an error message: Error: An invalid to ...

The value attribute in the HTML input tag being dynamically increased by JavaScript

Hi there, can someone help me figure out how to save changes to the value attribute of an HTML input tag that is being incremented by JavaScript? Currently, every time I click on a specific element, the input field should increase by one. The problem is th ...

What is the best way to retrieve a value from $http or $resource using this filter?

Within my HTML, I have a structure that looks like {{ i.userId | userName }}. Essentially, I am attempting to convert this into a name by utilizing a filter. However, I am encountering numerous challenges with the asynchronous function required to retrieve ...

The virtual method 'android.location.Location' was called in error

I'm using WL.Device.Geo.acquirePosition(onGeoLocationSuccess, onGeoLocationFailure, options) from MobileFirst to retrieve a device's location. Initially, everything works smoothly as I successfully obtain the location. However, after clearing t ...

Using TypeScript to validate the API response against specific types

I'm intrigued by the scenario where you expect a specific data type as a response from fetch / Axios / etc, but receive a different type instead. Is there a way to identify this discrepancy? interface HttpResponse<T> extends Response { parsed ...

An issue with the JSON format encountered in the D3 pack layout

I am completely new to d3 and have only been learning it for the past 3 days. I attempted to create a pack layout, but I encountered an issue where I couldn't call the translate (or transform) function based on the data in an external json file. The s ...

Exploring an alternative perspective on successful login in angular.js

Today was the beginning of my project to convert a website to angular.js. The main page is served by index.html and includes ng-view for other views such as login and signup. Working closely with a backend developer, I have successfully integrated a rest c ...

Identifying sluggish GPU performance on a mobile device using three.js: A guide for developers

My game runs extremely slow on older mobile devices like the Samsung Galaxy S4 and iPhone 5 when shadows are enabled. However, when shadows are turned off, performance improves significantly. Is there a way to detect a slow GPU in order to disable sh ...

When employing `queryParams` in Angular routing, the URL will automatically replace `%` with `%25`

When trying to use queryParams for navigation in Angular routing, I encountered an issue. <a routerLink='/master' [queryParams]="{query:'%US',mode:'text'}"><li (click)="search()">Search</li></a> The d ...

Unable to access image from JSON within Mobile Angular Ui utilizing Angular Js

Currently, I am able to retrieve various data from JSON such as Price and Display Order, but I am facing difficulty in fetching the image. HTML: <div ng-controller="chooseProductDescription"> <div ng-repeat="cat in productDescrip ...