Make and animate wave in canvas

后端 未结 3 682
夕颜
夕颜 2021-01-31 23:45

I’m looking for a way to create a wave in a shape designed in canvas. After much research I found something that is pretty close to what I want:

3条回答
  •  清酒与你
    2021-02-01 00:19

    Interference

    You can make a more realistic wave using interference.

    • Have one big wave (swell) running slowly with a big motion
    • Have another one or two smaller sine waves running (oscillators)
    • All with random amplitudes
    • Mix the waves horizontally using average and calculate the various points
    • Draw the result using a cardinal spline (or if the resolution is high you can just draw simple lines between the points instead).

    Use various parameters so you can adjust it live to find a good combination.

    You can also add oscillators to represent the z axis to make it more realistic in case you want to layer the waves to make a pseudo-3D wave.

    Example

    I cannot give you wave collision, fluid dynamics - that would be a bit too broad for SO but I can give you a fluid-ish wave example (as you have the point of each segment you can use that for collision detection).

    And example would be to create an oscillator object which you could set the various settings on such as amplitude, rotation speed (phase) etc.

    Then have a mixer function which mixes the result of these oscillators that you use.

    Live demo here (full-screen version here)

    Demo snapshot

    The oscillator object in this demo look like this:

    function osc() {
    
        /// various settings        
        this.variation = 0.4;  /// how much variation between random and max
        this.max       = 100;  /// max amplitude (radius)
        this.speed     = 0.02; /// rotation speed (for radians)
    
        var me  = this,        /// keep reference to 'this' (getMax)
            a   = 0,           /// current angle
            max = getMax();    /// create a temp. current max
    
        /// this will be called by mixer
        this.getAmp = function() {
    
            a += this.speed;   /// add to rotation angle
    
            if (a >= 2.0) {    /// at break, reset (see note)
                a = 0;
                max = getMax();
            }
    
            /// calculate y position
            return max * Math.sin(a * Math.PI) + this.horizon;
        }
    
        function getMax() {
            return Math.random() * me.max * me.variation +
                   me.max * (1 - me.variation);
        }
    
        return this;
    }
    

    This do all the setup and calculations for us and all we need to do is to call the getAmp() to get a new value for each frame.

    Instead of doing it manually we can use a "mixer". This mixer allows us to add as many oscillators we want to the mix:

    function mixer() {
    
        var d = arguments.length,  /// number of arguments
            i = d,                 /// initialize counter
            sum = 0;               /// sum of y-points
    
        if (d < 1) return horizon; /// if none, return
    
        while(i--) sum += arguments[i].getAmp(); /// call getAmp and sum
    
        return sum / d + horizon;  /// get average and add horizon
    }
    

    Putting this in a loop with a point recorder which shifts the point in one direction will create a fluid looking wave.

    The demo above uses three oscillators. (A tip in that regard is to keep the rotation speed lower than the big swell or else you will get small bumps on it.)

    NOTE: The way I create a new random max is not the best way as I use a break point (but simple for demo purpose). You can instead replace this with something better.

提交回复
热议问题