分散的宝丽来照片 - 使用 CSS3 和 jQuery 提供图像动画






4.95/5 (66投票s)
散射的宝丽来就像你拿起一盒旧照片,把它们全部倒在桌子上,然后随机挑选。这篇文章将介绍 CSS3 和 jQuery 实现的这个概念。
目录
- 引言
- 系统要求
- 宝丽来相框盒
- 加载图片
- 创建宝丽来图片
- 生成随机位置和旋转
- 定位和旋转宝丽来图片
- 使用 CSS3 盒阴影
- 拖放宝丽来图片
- 拿起一张宝丽来图片
- 放下宝丽来图片
- 通过 JavaScript 动画过渡
- 最终思考
- 历史
引言
散射的宝丽来是我为我的朋友 Janaina Rossi Moreira 开发的一个项目的第一个部分,她是一家名为“As Bárbaras do Multeatro”的巴西戏剧公司工作。
该项目的目标是宣传公司的作品,同时为访客提供美好的视听体验,依靠多媒体、视频、图像、诗歌和回忆,以更好地捕捉公司的艺术精髓。
导航照片集的方法有很多,通常是有序的。散射的宝丽来是一种不同的方法,就像你拿起一盒旧照片,把它们全部倒在桌子上,然后随机挑选。这篇文章将介绍这种技术,希望对你有所帮助。
系统要求
为了运行本文附带的散射的宝丽来示例,您必须拥有支持 CSS3 2D 转换的浏览器

- Internet Explorer 9 或更高版本
- Firefox
- Safari
- Chrome
宝丽来相框盒

宝丽来相框盒是容纳图片的元素,也将接收一些鼠标功能所需的事件。
您的页面中必须有一个 polaroid_box div,或者另一个可以作为图片容器的 div。
<body>
    <div class="polaroid_box">
    </div>
</body>
加载图片

图片加载到一个简单的字符串数组中,该数组描述了图片的 URL 路径。
$(document).ready(function () {
    var images = new Array();
    images[0] = 'Images/Barbaras1.jpg';
    images[1] = 'Images/Barbaras2.jpg';
    images[2] = 'Images/Barbaras3.jpg';
    images[3] = 'Images/Barbaras4.jpg';
    images[4] = 'Images/Barbaras5.jpg';
    images[5] = 'Images/Barbaras6.jpg';
    images[6] = 'Images/Barbaras7.jpg';
    images[7] = 'Images/Barbaras8.jpg';
    images[8] = 'Images/Barbaras9.jpg';
    images[9] = 'Images/Barbaras10.jpg';
    images[10] = 'Images/Barbaras11.jpg';
    images[11] = 'Images/Barbaras12.jpg';
    images[12] = 'Images/Barbaras13.jpg';
    images[13] = 'Images/Barbaras14.jpg';
    images[14] = 'Images/Barbaras15.jpg';
    images[15] = 'Images/Barbaras16.jpg';
    images[16] = 'Images/Barbaras17.jpg';
    images[17] = 'Images/Barbaras18.jpg';
    images[18] = 'Images/Barbaras19.jpg';
    images[19] = 'Images/Barbaras20.jpg';
    ...
数组加载完成后,您可以调用 createPolaroids 函数来渲染给定父元素内的所有必需 DOM 元素。
...
    maxZIndex = images.length;
    createPolaroids($('.polaroid_box'), images);
...
创建宝丽来图片

在这里,最重要的一点显然是创建 DOM 元素,它们将为我们渲染页面上的宝丽来图片。请注意,我们将大量依赖 jQuery JavaScript 框架。这将减少大部分繁琐的工作,并确保我们避免跨浏览器问题。
function createPolaroids($parentEl, images) {
    var parentWidth = $parentEl.width();
    var parentHeight = $parentEl.height();
    for (var i = 0; i < images.length; i++) {
        var polaroidHtml = 
            '<div id="polaroid' + (i + 1) + '" class="polaroid">' +
            '    <img id="img' + (i + 1) + '" class="photo" />' +
            '    <img src="Images/Polaroid_2.png" class="polaroid_photo" />' +
            '</div>';
        $parentEl.append(polaroidHtml);
        var $polaroid = $('#polaroid' + (i + 1));
        var $img = $('#img' + (i + 1));
        var width = $polaroid.width();
        var height = $polaroid.height();
        $img.attr('src', images[i]);
        $polaroid.css('z-index', 1000);
    }
...
生成随机位置和旋转
我们需要为每个宝丽来图片生成随机位置和旋转,否则就不能称之为“散射的”宝丽来。所以,这里我们要做的就是生成落在我们屏幕可接受边界内的数字(也就是说,图片被包含在一个框内,所以我们不希望图片超出屏幕)。
for (var i = 0; i < images.length; i++) {
    var $el = $('#polaroid' + (i + 1));
    var windowWidth = $(window).width();
    var windowHeight = $(window).height();
    var offset = (windowWidth - windowHeight) / 2;
    var width = $el.width();
    var height = $el.height();
    var rotation = Math.random() * maxRotationAngle * 2 - maxRotationAngle;
    var left = offset + Math.random() * (parentHeight - width);
    var top = Math.random() * (parentHeight - height);
    ...
}
现在我们已经收集了宝丽来图片所需的所有数据,我们创建一个新的宝丽来类型来保存这些信息。这将有助于封装数据并提供操作宝丽来图片的方法。
for (var i = 0; i < images.length; i++) {
    ...
    polaroids[i] = new polaroid(i + 1, $el, $parentEl, minScale, 
                   rotation, left, top, width / 2, height / 2, i);
}
然后将信息作为构造函数提供给 polaroid 函数,该函数将在实例中保存参数。
function polaroid(id, $el, $parentEl, scale, rotation, left, 
         top, xTransOrigin, yTransOrigin, zIndex) {
    this.id = id;
    this.$el = $el;
    this.$parentEl = $parentEl;
    this.scale = scale;
    this.rotation = rotation;
    this.originalScale = scale;
    this.originalRotation = rotation;
    this.originalLeft = left;
    this.originalTop = top;
    this.originalZIndex = zIndex;
    this.startRotation = rotation;
    this.left = left;
    this.top = top;
    this.xTransOrigin = xTransOrigin
    this.yTransOrigin = yTransOrigin;
    this.interval = null;
    this.isMoving = false;
    this.zIndex = zIndex;
    this.xOffset = 0;
    this.yOffset = 0;
}
定位和旋转宝丽来图片

加载完宝丽来图片数据后,我们就可以调用 updateLayout prototype 函数,它将相应地更新所需的 CSS 属性。
polaroid.prototype.updateLayout = function () {
    this.$el.css('left', this.left + 'px');
    this.$el.css('top', this.top + 'px');
    this.$el.css('z-index', this.zIndex);
    var transform = 
        'rotate(' + this.rotation + 'deg) scale(' + this.scale + ')';
    this.$el.css({ "-webkit-transform": transform, 
         "-moz-transform": transform, msTransform: transform });
};
值得注意的是,上面代码片段中的最后一行提供了主要浏览器的 CSS 转换。
- -webkit-transform:Chrome 和 Safari
- -moz-transform:Firefox
- msTransform:Internet Explorer
使用 CSS3 盒阴影

CSS3 盒阴影提供了勾勒我们图片的阴影。这是一个很棒的功能,可以使我们的页面在视觉上更具吸引力。但就像世界上大多数美好的事物一样,您应该适度使用它。盒阴影可能需要大量的处理,这取决于您如何使用它。我们的 polaroid CSS 类定义了一个盒阴影,偏移量为 (4,4),深度为 32 像素。
.polaroid
{
    ...
    -moz-box-shadow: 4px 4px 32px #000;
    -webkit-box-shadow: 4px 4px 32px #000;
    box-shadow: 4px 4px 32px #000;
    ...
}
正如您稍后在本文中将看到的,我们在宝丽来图片动画期间会移除盒阴影,以提高性能。作为一种建议,最好完全放弃使用盒阴影,而是用静态的 .png 图片(PNG 图片支持透明度)替换它们,从而提高性能。
拖放宝丽来图片
应用程序通过将宝丽来图片实例存储在 selectedPolaroid 变量中来知道哪个宝丽来图片正在被拖动。当您单击某个宝丽来图片时,该宝丽来图片的 mousedown 事件将被触发,并且 selectedPolaroid 将被存储。
$('.polaroid').mousedown(function (event) {
    var id = $(this).attr('id');
    var i = id.replace('polaroid', '');
    var polaroid = polaroids[i - 1];
    if (polaroid.scale == minScale) {
        polaroid.zIndex = maxZIndex++;
        polaroid.updateLayout();
        if (selectedPolaroid) {
            selectedPolaroid = null;
        }
        else {
            selectedPolaroid = polaroid;
            selectedPolaroid.xOffset = event.pageX - selectedPolaroid.left;
            selectedPolaroid.yOffset = event.pageY - selectedPolaroid.top;
        }
    }
    else {
        if (zoomedPolaroid.id == polaroid.id) {
            polaroid.drop();
        }
    }
});
从这一点开始,任何鼠标移动都将由宝丽来图片盒的 mousemove 事件触发,并且选定的宝丽来图片位置将被更新。
$polaroid_box.mousemove(function (event) {
    if (selectedPolaroid) {
        selectedPolaroid.left = event.pageX - selectedPolaroid.xOffset;
        selectedPolaroid.top = event.pageY - selectedPolaroid.yOffset;
        selectedPolaroid.updateLayout();
    }
});
当您释放鼠标按钮时,拖放操作将结束。此时,我们将 selectedPolaroid 变量设置为 null。
$polaroid_box.mouseup(function (event) {
    if (selectedPolaroid) {
        selectedPolaroid.originalLeft = selectedPolaroid.left;
        selectedPolaroid.originalTop = selectedPolaroid.top;
        selectedPolaroid = null;
    }
});
拿起一张宝丽来图片
在某个时候,您发现一张有趣的图片并想更近地查看它。所以您双击它。这将触发我们应用程序中的动画功能,将宝丽来图片带到前面。图片将看起来更大,并且完美地居中显示。这是通过一个动画完成的,这是此应用程序中更复杂的部分,稍后将进行详细解释。一旦图片被拿起,我们就会设置 zoomedPolaroid 变量,并等待用户放下它或双击另一张图片。
$('.polaroid').dblclick(function () {
    var id = $(this).attr('id');
    var i = id.replace('polaroid', '');
    var polaroid = polaroids[i - 1];
    if (zoomedPolaroid) {
        zoomedPolaroid.zIndex = maxZIndex++;
        zoomedPolaroid.updateLayout();
        zoomedPolaroid.drop(null, function () {
            zoomedPolaroid = null;
        });
    }
    if (polaroid.scale == minScale) {
        polaroid.originalZIndex = maxZIndex++;
        polaroid.zIndex = maxZIndex + 1000;
        polaroid.updateLayout();
        polaroid.pull(null, function () {
            zoomedPolaroid = polaroids[polaroid.id - 1];
            selectedPolaroid = null;
        });
    } else {
        polaroid.originalZIndex = maxZIndex++;
        polaroid.zIndex = maxZIndex++;
        polaroid.drop();
    }
});
放下宝丽来图片
当用户单击放大的宝丽来图片或双击另一张宝丽来图片时,它将被放下。这将启动反向动画,宝丽来图片将离开屏幕中心,回到其先前的位置、比例和旋转角度。
通过 JavaScript 动画过渡

动画是复杂的部分。它们需要目标比例、目标旋转角度和目标位置。由于宝丽来图片可能要移到中心或返回到其先前的位置,我们通过将核心功能封装在 animate 函数中来避免代码重复。
首先,我们计算差值(一个用于角度,另一个用于比例,以及两个用于位置的差值)。然后我们计算步长(即,在每次迭代中应用于当前动画值的增量)。然后我们启动 setInterval 函数来重新定位宝丽来图片,旋转它,并更新底层 DOM 元素,直到动画达到目标值。
polaroid.prototype.animate = function (targetScale, targetRotation, 
         targetLeft, targetTop, beginCallback, endCallback) {
    var pol = this;
    if (pol.isMoving)
        return;
    pol.isMoving = true;
    var parentWidth = this.$parentEl.width();
    var parentHeight = this.$parentEl.height();
    var polaroid = pol;
    if (beginCallback)
        beginCallback(polaroid);
    var angleDelta = targetRotation - polaroid.rotation;
    var scaleDelta = targetScale - polaroid.scale;
    var leftDelta = targetLeft - polaroid.left;
    var topDelta = targetTop - polaroid.top;
    var steps = animationSteps;
    var scaleStep = scaleDelta / steps;
    var rotationStep = angleDelta / steps;
    var leftStep = leftDelta / steps;
    var topStep = topDelta / steps;
    var interval;
    var rotation = polaroid.rotation;
    var scale = polaroid.scale;
    var left = polaroid.left;
    var top = polaroid.top;
    var $polaroid = $('#polaroid' + pol.id);
    $polaroid.css('z-index', 100);
    $polaroid.css({ "-moz-box-shadow": "0 0 0 #fff" });
    $polaroid.css({ "-webkit-box-shadow": "0 0 0 #fff" });
    $polaroid.css({ "box-shadow": "0 0 0 #fff" });
    var interval = setInterval(function () {
        if (
                (scaleStep > 0 && scale >= targetScale) ||
                (scaleStep < 0 && scale <= targetScale)
            ) {
            scale = targetScale;
        }
        $polaroid.css({ WebkitTransform: 'rotate(' + rotation + 
                        'deg) scale(' + scale + ')' });
        $polaroid.css({ '-moz-transform': 'rotate(' + rotation + 
                        'deg) scale(' + scale + ')' });
        $polaroid.css({ msTransform: 'rotate(' + rotation + 
                        'deg) scale(' + scale + ')' });
        $polaroid.css('left', left + 'px');
        $polaroid.css('top', top + 'px');
        if (scale == targetScale) {
            $polaroid.css({ "-moz-box-shadow": "4px 4px 32px #000" });
            $polaroid.css({ "-webkit-box-shadow": "4px 4px 32px #000" });
            $polaroid.css({ "box-shadow": "4px 4px 32px #000" });
            pol.isMoving = false;
            clearInterval(interval);
            rotation = targetRotation;
            scale = targetScale;
            left = targetLeft;
            top = targetTop;
            pol.rotation = targetRotation;
            pol.scale = targetScale;
            pol.left = targetLeft;
            pol.top = targetTop;
            pol.updateLayout();
            if (endCallback)
                endCallback(polaroid);
        }
        rotation += rotationStep;
        scale += scaleStep;
        left += leftStep;
        top += topStep;
    }, 5);
};
最终考虑
我希望您喜欢这篇文章和代码。我认为这里介绍的技术简单且有用。请在下面的评论部分告诉我您的想法。
历史
- 2011-08-28:初始版本。



