65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (66投票s)

2011 年 8 月 28 日

CPOL

5分钟阅读

viewsIcon

98438

downloadIcon

1660

散射的宝丽来就像你拿起一盒旧照片,把它们全部倒在桌子上,然后随机挑选。这篇文章将介绍 CSS3 和 jQuery 实现的这个概念。

目录

引言

散射的宝丽来是我为我的朋友 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:初始版本。
© . All rights reserved.