使用单个平面图像的假视差效果

时间:2013-01-15 16:38:40

标签: javascript image-processing canvas parallax

我正试图在JavaScript中实现'假'视差效果。描述我的意思真的很难,但我想尝试一下:

当我将鼠标悬停在页面上时,我希望页面上的内容可以移动。后面的东西应该比前面的东西少 - 通常Parallax effect - 除了我想使用平面图像这样做。现在,通常你必须在图像编辑器中剪掉东西并替换现在缺少的部分背景等等。但我认为我曾经以某种“假的”方式看到过这种情况:通过略微拉伸和压缩背景。

不幸的是,我无法找到这种效果的例子,但我很确定它与置换贴图有关。

这种东西有图书馆吗?或者至少有人知道我可以翻译成另一种语言的例子吗?

2 个答案:

答案 0 :(得分:0)

不确定你在问什么。但是我用spritely快速做了一个视差场景。它确实涉及多个图像层,但它们可以很容易地制作。 http://www.spritely.net/

还有一些像这样的视差解决方案。这种方法涉及重复相同的图像以创建深度。 http://www.stevefenton.co.uk/cmsfiles/assets/File/imageparallax.html

答案 1 :(得分:0)

乍一看,Phrogz link似乎没有相关性(之前我也进行了自己的研究时也点了相同的链接),但是当我深入研究它时,它是完全我在寻找什么。

不幸的是,它真的慢,所以它并不适合我想要的那种动画,特别是对于屏幕填充图像。通过做一些微优化,我得到了更快的速度,但最终这种情况对于今天的JavaScript来说似乎过于激烈。

无论如何,这就是我最终的结果。也许它可以帮助某人,或者有人想出如何加速它以便以30或60 fps的速度使用它(工人?)。 代码并不漂亮,因为我只是搞砸了一下。以此为出发点。

HTML:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
        <script src="displacement.js"></script>
    </head>
    <body>
        <script>
            (function() {
                'use strict';
                var source, map, target;
                var sourceCtx, mapCtx, targetCtx;
                var loaded = false;
                var x = 0, y = 0;
                var filter;

                function render() {
                    filter = new filters.DisplacementMap(
                            source,
                            map,
                            target,
                            new filters.Point(0, 0),
                            x,
                            y,
                            filters.ColorChannel.RED,
                            filters.ColorChannel.RED);
                    filter.draw();
                }

                (function init() {
                    var img = new Image();

                    function prepareCanvases() {
                        // Create canvases and add them to the document
                        source = document.createElement('canvas');
                        map = document.createElement('canvas');
                        target = document.createElement('canvas');
                        [target, source, map].forEach(function(canvas) {
                            canvas.setAttribute('width', img.width);
                            canvas.setAttribute('height', img.height);
                            document.body.appendChild(canvas);
                        });

                        // Get contexts
                        sourceCtx = source.getContext('2d');
                        mapCtx = map.getContext('2d');
                        targetCtx = target.getContext('2d');
                    }

                    // Load source image
                    (function loadImage() {
                        img.onload = function() {
                            prepareCanvases();
                            sourceCtx.drawImage(img, 0, 0);
                            img.onload = function() {
                                mapCtx.drawImage(img, 0, 0);
                                addEventListeners();
                                loaded = true;
                            };
                            // Put your displacement map here
                            img.src = 'jn-map-small.jpg';
                        };
                        // Put your image here
                        img.src = 'jn-edit-small.jpg';
                    }());

                    // Event listener
                    function addEventListeners() {
                        target.onmousemove = function(e) {
                            x = ((-e.offsetX / source.width) * 10) + 5;
                            y = ((-e.offsetY / source.height) * 10) + 5;
                            render();
                        };
                    }
                }());
            }());
        </script>
    </body>
</html>

displacement.js:

/**
 * Based on Romuald Quantin's code
 * @see http://www.soundstep.com/blog/2012/04/25/javascript-displacement-mapping/
 */
var filters = {} || filters;
(function() {

    filters.ColorChannel = {
        RED: 0,
        GREEN: 1,
        BLUE: 2,
        ALPHA: 3
    };

    filters.Point = function(x, y) {
        this.x = x || 0;
        this.y = y || 0;
    };

    filters.DisplacementMap = function(source, map, target, point, scaleX, scaleY, channelX, channelY) {
        this.source = source;
        this.map = map;
        this.target = target;
        this.sourceCtx = this.source.getContext("2d");
        this.mapCtx = this.map.getContext("2d");
        this.targetCtx = this.target.getContext("2d");
        this.point = point || new filters.Point();
        this.scaleX = scaleX || 0;
        this.scaleY = scaleY || 0;
        this.channelX = channelX || filters.ColorChannel.RED;
        this.channelY = channelY || filters.ColorChannel.RED;
        if (this.channelX !== 0 && this.channelX !== 1 && this.channelX !== 2 && this.channelX !== 3)
            this.channelX = filters.ColorChannel.RED;
        if (this.channelY !== 0 && this.channelY !== 1 && this.channelY !== 2 && this.channelY !== 3)
            this.channelY = filters.ColorChannel.RED;
    };

    var p = filters.DisplacementMap.prototype;

    p.draw = function() {
        var sourceData = this.sourceCtx.getImageData(0, 0, this.source.width, this.source.height);
        var mapData = this.mapCtx.getImageData(0, 0, this.map.width, this.map.height);
        var targetDataX = this.sourceCtx.getImageData(0, 0, this.source.width, this.source.height);
        var targetDataY = this.sourceCtx.getImageData(0, 0, this.source.width, this.source.height);
        var pixelsLength = mapData.data.length / 4;
        var colorValue,
                alphaValue,
                ratio,
                ratioWithAlpha,
                pixelShift,
                sourcePosition,
                targetPosition,
                x,
                y;
        var i = 0;
        var map = this.map;
        var point = this.point;
        var channelX = this.channelX;
        var channelY = this.channelY;
        var scaleX = this.scaleX;
        var scaleY = this.scaleY;
        var source = this.source;
        var target = this.target;
        while (i < pixelsLength) {
            x = ((i % map.width) + point.x) | 0;
            y = (((i / map.width) | 0) + point.y) | 0;
//            alphaValue = mapData.data[i * 4 + filters.ColorChannel.ALPHA];
//            ratioWithAlpha = ratio * (alphaValue / 0xFF);
            targetDataX.data = this.setPixels(targetDataX.data, (target.width * y) + x + ((((mapData.data[i * 4 + channelX]) / 0xFF * 2) - 1) * scaleX | 0), sourceData.data, (source.width * y) + x);
            i++;
        }
        i = 0;
        while (i < pixelsLength) {
            x = ((i % map.width) + point.x) | 0;
            y = (((i / map.width) | 0) + point.y) | 0;
//            alphaValue = mapData.data[i * 4 + filters.ColorChannel.ALPHA];
//            ratioWithAlpha = ratio * (alphaValue / 0xFF);
            targetDataY.data = this.setPixels(targetDataY.data, (target.width * (y + ((((mapData.data[i * 4 + channelY]) / 0xFF * 2) - 1) * scaleY | 0))) + x, targetDataX.data, (source.width * y) + x);
            i++;
        }
        this.targetCtx.putImageData(targetDataY, 0, 0);
    };

    p.setPixels = function(target, pos, source, i) {
        i = i * 4;
        pos = pos * 4;
        target[i] = source[pos];
        target[i + 1] = source[pos + 1];
        target[i + 2] = source[pos + 2];
        target[i + 3] = source[pos + 3];
        return target;
    };

})();