javascript - 水波纹 - 第一次无法加载图像

标签 javascript html ripple

我按照说明创建水波纹效果,我用我的图像替换了背景。运行效果还不错。但它有一个问题。第一次加载时,浏览器无法加载我的背景(我的图像),您必须重新加载浏览器(F5)或在地址栏上再次输入才能看到效果。怎么解决?

我的代码:

 <!DOCTYPE html>
<html>
<head>
    <title>Water Ripple HTML5</title>
    <meta name="author" content="Brent Dingle">
    <meta name="description" content="HTML5 canvas example of water ripple effect">

    <style >
        .waterCanvasStyle
        {
            border-width: 1px;
            border-style: solid;
            border-color:#a1a1d0;
            border-radius: 8px;
            box-shadow: #c6c6d0 4px 4px 10px;
        }
</style>
</head>
<body>
    <canvas id="waterCanvas0" width="400" height="400" >
        Your browser does not support the HTML5 canvas tag.
    </canvas>
    <script>
        var canvas      = document.getElementById('waterCanvas0');
        var ctx         = canvas.getContext('2d');
        var width       = canvas.width;
        var height      = canvas.height;
        var halfWidth   = width  >> 1;
        var halfHeight  = height >> 1;
        var size        = width * (height + 2) * 2;   // space for 2 images (old and new), +2 to cover ripple radius <= 3
        var delay       = 30;                                  // delay is desired FPS
        var oldIdx      = width;
        var newIdx      = width * (height + 3);       // +2 from above size calc +1 more to get to 2nd image
        var rippleRad   = 3;

        var rippleMap   = [];
        var lastMap     = [];
        var mapIdx;
        
        // texture and ripple will hold the image data to be displayed
        var ripple;
        var texture;
        
        // Any image can be used, but we will create a simple pattern instead
        // So need some variables to create the background/underwater image
        var stripeWidth = 25;
        var step        = stripeWidth * 2;
        var count       = height / stripeWidth;
        
        canvas.width = width;
        canvas.height = height;
        
        var img = new Image();
        img.src = "sea.jpg"; 
               
        // Here is a neat trick so you don't have to type ctx.blah over and over again
        with (ctx)
        {
            drawImage(img,0,0); 
            save();
            restore();
        }
        
        // Initialize the texture and ripple image data
        // Texture will never be changed
        // Ripple is what will be altered and displayed --> see run() function
        texture = ctx.getImageData(0, 0, width, height);
        ripple = ctx.getImageData(0, 0, width, height);
        
        // Initialize the maps
        for (var i = 0; i < size; i++)
        {
            lastMap[i]   = 0;
            rippleMap[i] = 0;
        }
        
        // -------------------------------------------------------
        // --------------------- Main Run Loop --------------
        // -------------------------------------------------------
        function run()
        {
            newframe();
            ctx.putImageData(ripple, 0, 0);
        }
        
        // -------------------------------------------------------
        // Drop something in the water at location: dx, dy
        // -------------------------------------------------------
        function dropAt(dx, dy)
        {
            // Make certain dx and dy are integers
            // Shifting left 0 is slightly faster than parseInt and math.* (or used to be)
            dx <<= 0;
            dy <<= 0;
            
            // Our ripple effect area is actually a square, not a circle
            for (var j = dy - rippleRad; j < dy + rippleRad; j++)
            {
                for (var k = dx - rippleRad; k < dx + rippleRad; k++)
                {
                    rippleMap[oldIdx + (j * width) + k] += 512;
                }
            }
        }
        
        // -------------------------------------------------------
        // Create the next frame of the ripple effect
        // -------------------------------------------------------
        function newframe()
        {
            var i;
            var a, b;
            var data, oldData;
            var curPixel, newPixel;
            
            // Store indexes - old and new may be misleading/confusing
            //               - current and next is slightly more accurate
            //               - previous and current may also help in thinking
            i = oldIdx;
            oldIdx = newIdx;
            newIdx = i;
            
            // Initialize the looping values - each will be incremented
            i = 0;
            mapIdx = oldIdx;
            
            for (var y = 0; y < height; y++)
            {
                for (var x = 0; x < width; x++)
                {
                    // Use rippleMap to set data value, mapIdx = oldIdx
                    // Use averaged values of pixels: above, below, left and right of current
                    data = (
                            rippleMap[mapIdx - width] + 
                            rippleMap[mapIdx + width] + 
                            rippleMap[mapIdx - 1] + 
                            rippleMap[mapIdx + 1]) >> 1;    // right shift 1 is same as divide by 2
                    
                    // Subtract 'previous' value (we are about to overwrite rippleMap[newIdx+i])
                    data -= rippleMap[newIdx + i];
   
                    // Reduce value more -- for damping
                    // data = data - (data / 32)
                    data -= data >> 5;
   
                    // Set new value
                    rippleMap[newIdx + i] = data;

                    // If data = 0 then water is flat/still,
                    // If data > 0 then water has a wave
                    data = 1024 - data;
        
                    oldData = lastMap[i];
                    lastMap[i] = data;
        
                    if (oldData != data)  // if no change no need to alter image
                    {
                        // Recall using "<< 0" forces integer value
                        // Calculate pixel offsets
                        a = (((x - halfWidth) * data / 1024) << 0) + halfWidth;
                        b = (((y - halfHeight) * data / 1024) << 0) + halfHeight;
                        
                        // Don't go outside the image (i.e. boundary check)
                        if (a >= width) a = width - 1;
                        if (a < 0) a = 0;
                        if (b >= height) b = height - 1;
                        if (b < 0) b = 0;

                        // Set indexes
                        newPixel = (a + (b * width)) * 4;
                        curPixel = i * 4;
                        
                        // Apply values
                        ripple.data[curPixel]       = texture.data[newPixel];
                        ripple.data[curPixel + 1] = texture.data[newPixel + 1];
                        ripple.data[curPixel + 2] = texture.data[newPixel + 2];
                    }
                    mapIdx++;
                    i++;
                }
            }
        }

        // -------------------------------------------------------
        // Select random location to create drops
        // So if user is doing nothing, water still
        // gets ripples.
        // -------------------------------------------------------
        function randomDrop()
        {
           // Make it a little, irregular in timing
           if ( Math.random() > 0.3 )
           {
                dropAt(Math.random() * width, Math.random() * height);
           }
        }
        // -------------------------------------------------------
        // Event handler for mouse motion
        // -------------------------------------------------------
        canvas.onmousemove = function(/* Event */ evt)
        {
            dropAt(evt.offsetX || evt.layerX, evt.offsetY || evt.layerY);
        }
        
        // -------------------------------------------------------
        // Begin our infinite loop
        // For user interaction and display updates
        // -------------------------------------------------------
        setInterval(run, delay);
        
        // -------------------------------------------------------
        // Create random ripples
        // Note: this is NOT at same rate as display refresh
        // -------------------------------------------------------
        setInterval(randomDrop, 1250);
        
    </script>
</body>

最佳答案

发生此类问题时的一个常见错误是您尝试在加载图像之前绘制图像。尝试利用 img.onload 函数等待图像加载完成所有绘图,然后开始运行主循环。

关于javascript - 水波纹 - 第一次无法加载图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18052488/

相关文章:

javascript - 在顶部创建 Electron 透明窗口,但可在程序下方单击

javascript - 向下滚动页面时粘性表格标题单元格?

html - 创建 3 列布局的方法 : Fluid - Fixed - Fluid

javascript - 根据变量值隐藏表行

javascript - 我尝试使用 emailjs,但出现此错误 "user id is required"。如何修复它?

javascript - 维基百科 API - 仅显示 4 个结果

android - 对 recyclerview 触摸事件没有链式 react

Android L Ripple Effect-如何在我的搜索栏上禁用拇指的波纹效果?

jquery - Phonegap/Ripple 模拟器。 Jquery 选择器无法在对象方法内工作

javascript - 正在接收数据但无法显示