找回密码
 立即注册(邮箱请乱填
搜索
查看: 447|回复: 0

翻滚吧徐驰!舰娘游泳特效全攻略

[复制链接]

514

鱿鱼币

8976

56

主题

站长

脸卫兵

积分
2107

人民解放军战狼外交官可口可乐SIM卡潜水蛆红卫兵大撒币习近平来世还做中国人晶格的徽记

门派:学习墙国

擂台:天下第12

与他切磋一把

查看他的属性

发表于 2025-8-31 20:11:51 | 显示全部楼层 |阅读模式
话说很久没写过代码类的文章了,这次送上的是“翻滚吧舰娘!”的代码攻略。所谓“翻滚吧舰娘!”,最早是“GOODSMILE”为了宣传两只浴室玩具“お風呂これくしょん 島風”和“お風呂これくしょん 赤城”而制作的网站特效。后来国内某11区AMAZON代购网站把该特效给搬上了自己主页,并在代码注释上把其命名为:翻滚吧舰娘!
sss.JPG


     最初在GOODSMILE官网看到这个特效时,个人已经觉得颇为有趣。而当看到萌购把其搬过来后,个人便也想弄一个放到自己BLOG上。最后工夫不负有心人,花了4天时间敲代码敲得脖子痛死后,终于成功把之折腾出来并放到了个人BLOG首页上了。

     虽然“翻滚吧舰娘!”的特效看起来非常简单,但其实有好几个细心留意才会发现的细节。例如舰娘游泳时并不是直线移动,而是会伴随着上下颠簸而左右摇晃。又例如舰娘不仅可以用鼠标捉起四处飞,还会随着浏览器窗口大小改变而有相应的动画。

     正因为有这么多细节,刚开始个人还以为是个很简单的网页特效。结果后面越写越多,搞了4天才完成。其实个人最开始是想直接搬萌购的源代码,但点开一看就傻眼了,以个人的水平是完全看不懂……说实在我就只会个document.getelementbyid,萌购源代码那种商业级的水平,是连搬都不懂得怎么去搬,所以最后只能网上搜着自己重新写了个。虽然代码看起来比较脏乱,全局变量也四处乱飞,但至少是把效果给实现出来了。下面便是素材和源码:
-------------华丽的分割线-------------

*素材1:舰娘·徐驰(kankore-bath-shimakaze.png)
kankore-bath-shimakaze.png


*素材2:舰娘·赤城(kankore-bath-akagi.png)

kankore-bath-akagi.png

*素材3:海水(kankore-bath-water.png)
kankore-bath-water.png

源代码:新建记事本并把下面代码全部复制进去,另存为htm文件后与上面3张素材图片放到同一文件夹下,双击打开便可以直接运行“翻滚吧舰娘!”网页特效。


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <style type="text/css">
        .water
        {
            background-repeat: repeat;
            width: 2760px;
            height: 49px;
            position: absolute;
            bottom: 0px;
            opacity: 0.7;
        }
        .ship
        {
            width: 170px;
            height: 170px;
            position: absolute;
            top: -180px;
            left: -170px;
            cursor: pointer;
        }
        .copyright
        {
            width: 550px;
            position: absolute;
            bottom: 0px;
            top: -18px;
            left: 5px;
            text-align: left;
            font-family: 微软雅黑;
            font-size: 10px;
            color: #FFFFFF;
            cursor: pointer;
        }
    </style>
    <title>翻滚吧舰娘!</title>
</head>
<body>
    <div id="kankore_bath" style="position: fixed; bottom: 0; left: 0; opacity: 1;">
        <div style="background-image: url(kankore-bath-shimakaze.png);" id="shimakaze" class="ship">
        </div>
        <div style="background-image: url(kankore-bath-akagi.png);" id="akagi" class="ship">
        </div>
        <div>
            <div style="background-image: url(kankore-bath-water.png); left: -1380px;" id="kankore_bath_water1"
                class="water">
            </div>
            <div style="background-image: url(kankore-bath-water.png); left: 1380px;" id="kankore_bath_water2"
                class="water">
            </div>
        </div>
        <div class="copyright" id="copyright">
            Kankore ©2014 DMM.com/KADOKAWA GAMES All Rights Reserved. / Animation by ©GoodSmileCompany</div>
    </div>
    <script language="javascript" type="text/javascript">
        // 初始化动画
        $(document).ready(function () {
            //            if (document.URL == "http://hero32167.blog126.fc2.com/" || document.URL == "http://hero32167.blog126.fc.com/") {
            document.ondragstart = function () { return false; }
            setDirection();
            setShip();
            animateWater();
            animateShip();
            //            }
            //            else {
            //                $("#kankore_bath").css("display", "none");
            //            }
        })


        // 设置水流方向
        var direction;
        function setDirection() {
            var s = Math.random();
            if (s <= 0.5) {
                direction = "+";
            }
            else {
                direction = "-";
            }
        }


        // 初始化舰娘
        var ship = "#shimakaze";
        function setShip() {
            if ($(ship).is(":animated")) {
                stopShip();
            }
            $("#shimakaze").css({ visibility: "hidden" });
            $("#akagi").css({ visibility: "hidden" });
            var s = Math.random();
            if (s <= 0.5) {
                ship = "#shimakaze";
            }
            else {
                ship = "#akagi";
            }
            if (direction == "+") {
                $(ship).css("left", "-170px");
            }
            else {
                $(ship).css("left", getWindowWidth() + "px");
            }
            $(ship).css({ visibility: "visible" });
        }


        // 鼠标事件
        var offsetX, offsetY, mouse_down_flag = false, mouse_move_flag = false;
        $(".ship").click(function () { }).mousedown(function (e) {
            stopShip();
            animateDargShip();
            mouse_down_flag = true;
            mouse_move_flag = false;
            offsetX = e.pageX - parseInt($(ship).css("left"));
            offsetY = e.pageY - parseInt($(ship).css("top"));
        });
        $(document).mousemove(function (e) {
            if (mouse_down_flag) {
                mouse_move_flag = true;
                var x = e.pageX - offsetX;
                var y = e.pageY - offsetY;
                $(ship).css({ top: y, left: x });
            }
        }).mouseup(function () {
            if (mouse_down_flag) {
                if (!mouse_move_flag) {
                    if (ship == "#akagi") {
                        window.open("http://www.goodsmile.info/ja/product/4213/%E3%81%8A%E9%A2%A8%E5%91%82%E3%81%93%E3%82%8C%E3%81%8F%E3%81%97%E3%82%87%E3%82%93+%E8%B5%A4%E5%9F%8E.html", "_blank");
                    }
                    else {
                        window.open("http://www.goodsmile.info/ja/product/4212/%E3%81%8A%E9%A2%A8%E5%91%82%E3%81%93%E3%82%8C%E3%81%8F%E3%81%97%E3%82%87%E3%82%93+%E5%B3%B6%E9%A2%A8.html", "_blank");
                    }
                }
                stopDragShip();
                animateResetShip();
                mouse_down_flag = false;
                mouse_move_flag = false;
            }
        });


        // 窗口事件
        var windowHeight = getWindowHeight(), newWindowHeight;
        var isResizing = false;
        window.onresize = function () {
            if (!isResizing) {
                newWindowHeight = getWindowHeight();
                if (newWindowHeight != windowHeight) {
                    stopShip();
                    isResizing = true;
                    var h = 1.2 * (newWindowHeight - windowHeight);
                    $(ship).css({ top: "-=" + h + "px" });
                    $(ship).animate({ left: direction + "=10px" }, 500, "linear");
                    setTimeout("animateResetShip()", 500);
                    windowHeight = newWindowHeight;
                }
            }
            setTimeout(function () {
                isResizing = false;
            }, 100);
        }


        // 获取窗口高度
        function getWindowHeight() {
            var winHeight;
            if (window.innerHeight) {
                winHeight = window.innerHeight;
            }
            else if ((document.body) && (document.body.clientHeight)) {
                winHeight = document.body.clientHeight;
            }
            if (document.documentElement && document.documentElement.clientHeight) {
                winHeight = document.documentElement.clientHeight;
            }
            return winHeight;
        }


        // 获取窗口宽度
        function getWindowWidth() {
            var winWidth;
            if (window.innerWidth) {
                winWidth = window.innerWidth;
            }
            else if ((document.body) && (document.body.clientWidth)) {
                winWidth = document.body.clientWidth;
            }
            if (document.documentElement && document.documentElement.clientWidth) {
                winWidth = document.documentElement.clientWidth;
            }
            return winWidth;
        }


        // 舰娘摇晃动画
        var timeRotate;
        function animateDargShip() {
            animateRotateShip("2");
            setTimeout('animateRotateShip("-2")', 500);
            timeRotate = setTimeout("animateDargShip()", 1000);
        }


        // 设置舰娘摇晃参数
        function animateRotateShip(d) {
            $(ship).css("-moz-transition", "-moz-transform 1s");
            $(ship).css("-moz-transform", " rotate(" + d + "deg)");
            $(ship).css("-webkit-transition", "-webkit-transform 1s");
            $(ship).css("-webkit-transform", " rotate(" + d + "deg)");
            $(ship).css("-o-transition", "-o-transform 1s");
            $(ship).css("-o-transform", " rotate(" + d + "deg)");
            $(ship).css("transition", "transform 1s");
            $(ship).css("transform", " rotate(" + d + "deg)");
        }


        // 停止舰娘摇晃
        function stopDragShip() {
            clearTimeout(timeRotate);
        }


        // 重置舰娘动画
        function animateResetShip() {
            var y = parseInt($(ship).css("top"));
            if (y != 180) {
                if (y < -280) {
                    $(ship).animate({ top: -160 + "px" }, 700, "linear");
                    $(ship).animate({ top: -170 + "px" }, 100, "linear");
                }
                else if (y >= -280 && y < -180) {
                    $(ship).animate({ top: -170 + "px" }, 700, "linear");
                }
                else {
                    $(ship).animate({ top: -180 + "px" }, 500, "linear");
                }
            }
            animateShip();
        }


        // 舰娘动画
        function animateShip() {
            if (direction == "+") {
                if (parseInt($(ship).css("left")) > $(document.body).width() + 30) {
                    stopShip();
                    setShip();
                }
            }
            else {
                if (parseInt($(ship).css("left")) < -200) {
                    stopShip();
                    setShip();
                }
            }
            var d = Math.random() * 4 - 2;
            animateRotateShip(d);
            var x = parseInt($(ship).css("left"));
            if (direction == "+") {
                x += 10;
            }
            else {
                x -= 10;
            }
            var y = 3 * Math.sin(x) - 180;
            $(ship).animate({ left: x + "px", top: y + "px" }, 1000, "linear", animateShip);
        }


        // 停止舰娘动画
        function stopShip() {
            $(ship).stop(true);
        }


        // 水流动画
        function animateWater() {
            var l1 = parseInt($("#kankore_bath_water1").css("left"));
            var l2 = parseInt($("#kankore_bath_water2").css("left"));
            if (direction == "+") {
                if (l1 > 0 && l2 > 0) {
                    if (l1 > l2) {
                        $("#kankore_bath_water1").css("left", (l2 - 2760) + "px");
                    }
                    else {
                        $("#kankore_bath_water2").css("left", (l1 - 2760) + "px");
                    }
                }
            }
            else {
                if (l1 < 0 && l2 < 0) {
                    if (l1 < l2) {
                        $("#kankore_bath_water1").css("left", (l2 + 2760) + "px");
                    }
                    else {
                        $("#kankore_bath_water2").css("left", (l1 + 2760) + "px");
                    }
                }
            }
            $("#kankore_bath_water1").animate({ left: direction + "=2px" }, 30);
            $("#kankore_bath_water2").animate({ left: direction + "=2px" }, 30, animateWater);
        }


        // 设置点击版权后关闭全部动画
        $("#copyright").click(function () {
            $("#kankore_bath").animate({ opacity: "0" }, 2000);
            stopShip();
            $("#kankore_bath_water1").stop(true);
            $("#kankore_bath_water2").stop(true);
            setTimeout(function () { $("#kankore_bath").css("display", "none"); }, 3000);
            window.open("http://www.goodsmile.info", "_blank");
        });
    </script>
</body>
</html>


-------------华丽的分割线-------------


     接下来是代码和制作思路解释的时间。要实现“翻滚吧舰娘!”特效,涉及到的技术有:HTML、JavaScript(下简称JS)和CSS。HTML就不用说了,关键是JS和CSS。特效动画都需要用到前者派生的jQuery和后者最新版的CSS3来实现。虽然各种名词看起来很吓人,但这里只涉及到最基础的东西,所以上手难度其实非常低。下面先来看该特效的特点明细:


01、水:循环流动、透明
02、舰:向固定方向上下浮沉的移动
03、舰:到达尽头时从起点再次出发
04、舰:移动时左右摇晃
05、舰:鼠标拖动时跟随鼠标移动
06、舰:跟随鼠标拖移时左右摇晃
07、舰:单击打开官方页面
08、舰:鼠标松开时掉落水面
09、舰:窗口大小变化时动画回到水面
10、总:随机水流方向和浮动方向
11、总:随机选择舰娘
12、总:点击版权声明时打开官网,全动画停止消失
13、总:整体固定在窗口的底部,而非网页的底部
14、总:只在BLOG首页显示该特效


-----------------01-------------------


*01、水:循环流动、透明
     水是最开始做的部分,其最难点是循环流动。个人最开始的思路是:首先获取当前窗口的宽度,然后用该宽度除以素材中海水图的宽度,得出需要多少张海水图才能水平铺满整个窗口。其后使用JS动态生成这么多张海水图,并使它们全部向固定方向平移。当最前头的一张完全移出窗口后,把其移动到海水队列的末尾。


     该思路来源于网上的“无缝滚动图片特效”,具体代码网上也可以下载到。但实际编写时却先遇到个问题:假设有3张海水图,如何把其放到同一水平面上。个人最开始的方法是使用margin,强制上下移动DIV来达到统一水平面。但后来参考了萌购的代码,决定还是放弃margin而使用绝对定位。两者效果其实是一样的,但我隐约记得margin在IE各版本下是有问题的,而绝对定位操作也更自由,所以最后选择了后者。


     接着是复制海水图。最开始的思路是使用jQuery的复制元素功能,但网上那示例硬是没看明白。后来苦思了一段时间,想出了个简单方法:不需要根据窗口最大宽度来动态生成海水图,而是直接定义两张超长的海水图,然后循环移动就行了,HTML和CSS代码如下:


.water
{
    background-repeat: repeat;
    width: 2760px;
    height: 49px;
    position: absolute;
    bottom: 0px;
    opacity: 0.7;
}


<div style="background-image: url(kankore-bath-water.png); left: -1380px;" id="kankore_bath_water1" class="water"></div>
<div style="background-image: url(kankore-bath-water.png); left: 1380px;" id="kankore_bath_water2" class="water"></div>


     上述代码定义了两张宽度为2760PX的完全一样的海水图。其中第一张定位为前半部分隐藏在窗口左面,而第二张则左侧紧跟在第一张之后。如果以窗口大小为2760PX看的话,那第二张的后半部分其实也隐藏在窗口的右面。现在高清标准是1080P,也就是1920x1080,所以这里使用2760PX是足够的。


     至于图片方面,最开始个人是以标准格式<div><img /></div>来编写的。但后来发现要实现2760PX的效果,直接把图片定义为DIV的背景图片更好。注意图片并不是按DIV宽度来拉伸,而是重复显示,所以需要把DIV定义为和图片一样的高度。


     接着是关键的循环动作,以jQuery来实现动画特效,并加上超出屏幕时的重新定位判断,代码如下:


function animateWater() {
    var l1 = parseInt($("#kankore_bath_water1").css("left"));
    var l2 = parseInt($("#kankore_bath_water2").css("left"));
    if (direction == "+") {
        if (l1 > 0 && l2 > 0) {
            if (l1 > l2) {
                $("#kankore_bath_water1").css("left", (l2 - 2760) + "px");
            }
            else {
                $("#kankore_bath_water2").css("left", (l1 - 2760) + "px");
            }
        }
    }
    else {
        if (l1 < 0 && l2 < 0) {
            if (l1 < l2) {
                $("#kankore_bath_water1").css("left", (l2 + 2760) + "px");
            }
            else {
                $("#kankore_bath_water2").css("left", (l1 + 2760) + "px");
            }
        }
    }
    $("#kankore_bath_water1").animate({ left: direction + "=2px" }, 30);
    $("#kankore_bath_water2").animate({ left: direction + "=2px" }, 30, animateWater);
}


     首先$("#").css("")是jQuery的格式,等同于JS的document.getElementById("").style。而parseInt()函数则是取字符串中的第一串数字。这段代码的意思是,若海水向右流动,并两张海水图的左边线相对窗口左侧定位都为正数时,把右边的一张移去左边那张的末尾。若海水向左流动,两张图的左边线相对窗口左侧都是负数时,则把左边的一张移去右边那张的末尾。从而形成“循环”效果。


     移动海水图的代码使用了jQuery的animate,也就是$("#").animate({},),后面跟随的数字代表在多少毫秒内完成。一般动画都是30帧,也就是一秒钟有30张图片,这里30毫秒动作一次,便是近似于30帧的效果。大家可以试着调整这个数字,会发现要不动画变得飞快,要不就会卡顿而使动画变得不流畅。


     这里还使用了“递归算法”,一种听起来很牛13但其实很简单的算法,也就是一个函数结束时,再调用函数自己本身。上面代码最后一句animate参数中,便定义了动画结束后再调用一次整个函数,从而达到整套动画的循环播放。


     附带一提,要使用jQuery的话,请在<head></head>中加入这句:<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>


--------------02、03------------------


*02、舰:向固定方向上下浮沉的移动
*03、舰:到达尽头时从起点再次出发


     完成水流动画后,那舰娘的动画就简单了,原理都是一样。这里的难点是上下浮沉,所谓上下浮沉,其实从运动线来说就是波型,这个我也是从萌购源代码中发现的。只要定义舰娘的运动轨迹为正弦波型就行了,具体代码如下:


function animateShip() {
    if (direction == "+") {
        if (parseInt($(ship).css("left")) > $(document.body).width() + 30) {
            stopShip();
            setShip();
        }
    }
    else {
        if (parseInt($(ship).css("left")) < -200) {
            stopShip();
            setShip();
        }
    }
    var d = Math.random() * 4 - 2;
    animateRotateShip(d);
    var x = parseInt($(ship).css("left"));
    if (direction == "+") {
        x += 10;
    }
    else {
        x -= 10;
    }
    var y = 3 * Math.sin(x) - 180;
    $(ship).animate({ left: x + "px", top: y + "px" }, 1000, "linear", animateShip);
}


     运动轨迹定义是在这句:var y = 3 * Math.sin(x) - 180。这段代码后半段的意思是,若水流方向向右,则舰娘在X轴上向右移动,反之向左。而舰娘的Y轴则根据3 * Math.sin(x) - 180的公式来计算获得。最后通过jQuery的animate来实现移动动画,注意参数“linear”可以让动画过程变得更为平滑,但真正涵义其实我到现在都不知道……


     至于这段代码的前半部分,则是根据舰娘左边线相对窗口左侧的距离来定位舰娘。若舰娘完全离开当前窗口,则重置舰娘以达到“到达尽头时从起点再次出发”的效果。关于重置函数可见下面第11点的“随机选择舰娘”部分。


-----------------04-------------------


*04、舰:移动时左右摇晃
     所谓左右摇晃,其实就是旋转图片,需要用到CSS3中的transform。而为了实现渐变效果,则需要用到CSS中的transition。由于浏览器对这些参数支持都不同,所以需要分别定义。其中IE9及以下的版本都不支持transition,IE8及以下的版本不支持transform,IE10及以上版本则全部支持。具体代码如下:


function animateRotateShip(d) {
    $(ship).css("-moz-transition", "-moz-transform 1s");
    $(ship).css("-moz-transform", " rotate(" + d + "deg)");
    $(ship).css("-webkit-transition", "-webkit-transform 1s");
    $(ship).css("-webkit-transform", " rotate(" + d + "deg)");
    $(ship).css("-o-transition", "-o-transform 1s");
    $(ship).css("-o-transform", " rotate(" + d + "deg)");
    $(ship).css("transition", "transform 1s");
    $(ship).css("transform", " rotate(" + d + "deg)");
}


     这里稍微说回上面的水流动画,萌购并不是用jQuery的animate,而是使用纯CSS3的transition实现。这就导致萌购的海水在IE9及以下版本是不会流动的……接下来是左右摇晃的实现,其实就是直接调用上面的函数。通过设置参数2和-2,代表旋转角度为2度,并在其后回复原始角度。


var timeRotate;
function animateDargShip() {
    animateRotateShip("2");
    setTimeout('animateRotateShip("-2")', 500);
    timeRotate = setTimeout("animateDargShip()", 1000);
}


     setTimeout()函数的功能是延迟一段时间后再执行代码,这里第二句的作用是延迟500毫秒才旋转回原始角度。而最后一句则是1秒后再递归算法的重复调用自己,以达到持续左右摇晃的效果。这里还定义了一个全局变量timeRotate,其作用于下面的停止摇晃函数:


function stopDragShip() {
    clearTimeout(timeRotate);
}


     顺带一提,由于IE9只支持transform而不支持transition,所以IE9下舰娘的摇晃动画会显得充满硬直而没有渐变的效果。这个问题有望在IE10时可以彻底解决。


--------------05、06、07--------------


*05、舰:鼠标拖动时跟随鼠标移动
*06、舰:跟随鼠标拖移时左右摇晃
*07、舰:单击打开官方页面


     当点击舰娘并拖动鼠标时,舰娘会跟随鼠标移动。这是个很有趣的设计,但对于个人来说是完全不懂得怎么去实现……所以最后是在网上找到一段合适的代码,然后根据萌购源代码改写而成:


var offsetX, offsetY, mouse_down_flag = false, mouse_move_flag = false;
$(".ship").click(function () { }).mousedown(function (e) {
    stopShip();
    animateDargShip();
    mouse_down_flag = true;
    mouse_move_flag = false;
    offsetX = e.pageX - parseInt($(ship).css("left"));
    offsetY = e.pageY - parseInt($(ship).css("top"));
});
$(document).mousemove(function (e) {
    if (mouse_down_flag) {
    mouse_move_flag = true;
    var x = e.pageX - offsetX;
    var y = e.pageY - offsetY;
    $(ship).css({ top: y, left: x });
    }
}).mouseup(function () {
    if (mouse_down_flag) {
        if (!mouse_move_flag) {
            if (ship == "#akagi") {
                window.open("http://www.goodsmile.info/ja/product/4213/%E3%81%8A%E9%A2%A8%E5%91%82%E3%81%93%E3%82%8C%E3%81%8F%E3%81%97%E3%82%87%E3%82%93+%E8%B5%A4%E5%9F%8E.html", "_blank");
            }
            else {
                window.open("http://www.goodsmile.info/ja/product/4212/%E3%81%8A%E9%A2%A8%E5%91%82%E3%81%93%E3%82%8C%E3%81%8F%E3%81%97%E3%82%87%E3%82%93+%E5%B3%B6%E9%A2%A8.html", "_blank");
            }
        }
        stopDragShip();
        animateResetShip();
        mouse_down_flag = false;
        mouse_move_flag = false;
    }
});


     所谓的鼠标拖动,原理其实是这样的:根据鼠标初次点击的位置和移动后的位置,利用X轴和Y轴的距离差来让舰娘的运动轨迹形成一条与之平行的平行线。而这两条运动轨迹线连起来的话正好是一个平行四边形。


     注意的是,上述代码虽然都是事件处理函数,但事件主体却并不全是舰娘。所以为了限制事件的触发条件,这里定义了全局变量mouse_down_flag和mouse_move_flag。而HTML方面也是使用了DIV背景图片,而非<div><img /></div>格式,使用后者的话会使得整个拖移动画变得非常卡。


     摇晃舰娘就不说了,使用的是上面第4点的函数。至于单击舰娘会打开官方页面的话,使用的是window.open()函数,后面参数“_blank”意思是在新窗口打开网页。


-----------------08-------------------


*08、舰:鼠标松开时掉落水面
     当鼠标拖移并松开后,舰娘一般都不在正常水平线上了,所以需要一段动画来实现其回归水平线,具体代码如下:


function animateResetShip() {
    var y = parseInt($(ship).css("top"));
    if (y != 180) {
        if (y < -280) {
            $(ship).animate({ top: -160 + "px" }, 700, "linear");
            $(ship).animate({ top: -170 + "px" }, 100, "linear");
        }
        else if (y >= -280 && y < -180) {
            $(ship).animate({ top: -170 + "px" }, 700, "linear");
        }
        else {
            $(ship).animate({ top: -180 + "px" }, 500, "linear");
        }
    }
    animateShip();
}


     这里的区别只在于若舰娘被拖移得太高时,掉落动画会有个水流紧急缓冲的动画特效。但有一点要注意,这个动画里不能同时写left的变化。个人最初是同时写上left动画的,却导致后面舰娘回归水平线后移动时,无法正常获得left的值。也就是说这个掉落水面动画若改变left,会导致后面的动画与该动画的left值不等。具体原因貌似涉及到jQuery的动画队列什么的,个人并没有就此深究下去……


-----------------09-------------------


*09、舰:窗口大小变化时动画回到水面
     这是最让我头痛的一个部分,由于萌购实现了这个效果,所以个人也只能想方法做出来。这效果并不是通用效果,网上也没有。个人最开始以为是根据屏幕来锁定舰娘位置,从而不受窗口变化而改变位置。但事实上这是做不到的,因为根本没有屏幕位置的参数……


     最后在细心观察下,个人才发现自己理解错误。这个动画效果的本质其实是:若窗口宽度发生改变,舰娘的水平位置便跟随绝对定位变化。若窗口高度发生改变,则根据变化值的比例来调整舰娘距离水平线的高度。舰娘在窗口大小变化前后,其实相对屏幕的位置是变化的。只不过只要比例合适的话,那高度变化可以相对显得不那么明显。具体实现代码如下:


var windowHeight = getWindowHeight(), newWindowHeight;
var isResizing = false;
window.onresize = function () {
    if (!isResizing) {
        newWindowHeight = getWindowHeight();
        if (newWindowHeight != windowHeight) {
            stopShip();
            isResizing = true;
            var h = 1.2 * (newWindowHeight - windowHeight);
            $(ship).css({ top: "-=" + h + "px" });
            $(ship).animate({ left: direction + "=10px" }, 500, "linear");
            setTimeout("animateResetShip()", 500);
            windowHeight = newWindowHeight;
        }
    }
    setTimeout(function () {
        isResizing = false;
    }, 100);
}


     代码整体不复杂,不过有几个细节:1、高度变化调整舰娘高度的比例为1.2倍;2、若舰娘浮空的话,则其会先平移一段距离再回归水平线;3、回归水平线的动画使用的是上面第8点的函数;4、窗口变化事件在IE有BUG,会瞬间触发两次,网上对此问题描述为“IE浏览器onresize事件执行多次”。所以这里设置了全局变量isResizing作条件,限制事件函数在100毫秒内只能执行一次,以修复该BUG。


     下面再附上网上直接搬下来的,根据不同浏览器情况去获取窗口高度和宽度的通用型函数:


function getWindowHeight() {
    var winHeight;
    if (window.innerHeight) {
        winHeight = window.innerHeight;
    }
    else if ((document.body) && (document.body.clientHeight)) {
        winHeight = document.body.clientHeight;
    }
    if (document.documentElement && document.documentElement.clientHeight) {
        winHeight = document.documentElement.clientHeight;
    }
    return winHeight;
}


function getWindowWidth() {
    var winWidth;
    if (window.innerWidth) {
        winWidth = window.innerWidth;
    }
    else if ((document.body) && (document.body.clientWidth)) {
        winWidth = document.body.clientWidth;
    }
    if (document.documentElement && document.documentElement.clientWidth) {
        winWidth = document.documentElement.clientWidth;
    }
    return winWidth;
}


-----------------10-------------------


*10、总:随机水流方向和浮动方向
     要做随机水流方向很简单,只要使用随机函数Math.random()获得随机值,然后根据值去定义水流方向就行了。这里定义水流方向的时候要小心一点,因为其不仅会影响水流的方向,还会一并影响舰娘的移动方向。如果弄错的话,那就可能会出现水流和舰娘方向相反的BUG。


     个人的思路为,首先定义水流方向direction为:向右则为“+”,向左则为“-”,然后通过随机数去决定水流方向,再根据方向来编写所有舰娘的动画函数。为什么方向取“+”和“-”,个人最初是想利用这个变量来直接定义动画参数,而不写IF来作判断。但可惜最后总览代码,发现并没有实现到想象中的效果……设置水流方向代码如下:


var direction;
function setDirection() {
    var s = Math.random();
    if (s <= 0.5) {
        direction = "+";
    }
    else {
        direction = "-";
    }
}


-----------------11-------------------


*11、总:随机选择舰娘
     只要懂得设置随机水流方向,那随机选择舰娘就很简单了,在选择舰娘的同时还可以顺便把其初始化。开始默认是所有舰娘都为不可见状态,只有被选中后才会显示出来,具体代码如下:


var ship = "#shimakaze";
function setShip() {
    if ($(ship).is(":animated")) {
        stopShip();
    }
    $("#shimakaze").css({ visibility: "hidden" });
    $("#akagi").css({ visibility: "hidden" });
    var s = Math.random();
    if (s <= 0.5) {
        ship = "#shimakaze";
    }
    else {
        ship = "#akagi";
    }
    if (direction == "+") {
        $(ship).css("left", "-170px");
    }
    else {
        $(ship).css("left", getWindowWidth() + "px");
    }
    $(ship).css({ visibility: "visible" });
}


     注意这里定义了全局变量ship,用来下面所有函数中代替$()的ID用。不过有个地方出了问题,那就是上面第5点的部分。在绑定鼠标事件时$(ship).click()报错,提示空值。这是因为事件绑定是在页面加载时进行的,但全局变量ship是在页面加载完成后,运行当中JS代码时才定义的,所以会出现没有找到对象的情况。个人的解决方法是:第5点的函数定义从$(ship).click()改为$(".ship").click(),通过CSS名去绑定所有的舰娘就可以了。下面是舰娘的HTML和CSS代码:


.ship
{
    width: 170px;
    height: 170px;
    position: absolute;
    top: -180px;
    left: -170px;
    cursor: pointer;
}


<div style="background-image: url(kankore-bath-shimakaze.png);" id="shimakaze" class="ship"></div>
<div style="background-image: url(kankore-bath-akagi.png);" id="akagi" class="ship"></div>


     这里有两个细节:1、图片地址记得写正确,不要搞出硬盘图来了;2、cursor: pointer;的意思是当鼠标移DIV上面时,其图示会变成手指状,实现让DIV变超链接的大变身。


-----------------12-------------------


*12、总:点击版权声明时打开官网,全动画停止消失
     这个是个人额外做的,原理在上面的函数都有,所以并没有花费太大功夫便完成了,具体代码如下:


.copyright
{
    width: 550px;
    position: absolute;
    bottom: 0px;
    top: -18px;
    left: 5px;
    text-align: left;
    font-family: 微软雅黑;
    font-size: 10px;
    color: #FFFFFF;
    cursor: pointer;
}


<div class="copyright" id="copyright">
    Kankore ©2014 DMM.com/KADOKAWA GAMES All Rights Reserved. / Animation by ©GoodSmileCompany</div>


$("#copyright").click(function () {
    $("#kankore_bath").animate({ opacity: "0" }, 2000);
    stopShip();
    $("#kankore_bath_water1").stop(true);
    $("#kankore_bath_water2").stop(true);
    setTimeout(function () { $("#kankore_bath").css("display", "none"); }, 3000);
    window.open("http://www.goodsmile.info", "_blank");
});


     CSS属性opacity,其作用是定义透明度,在上面第1点定义海水时也有用到。IE8及以下版本是不支持这个属性的,它们使用的是filter:alpha(opacity=50)。但只要使用这个属性,图片虽然能实现透明,却同时会产生非常恐怖的锯齿。所以个人最后决定还是放弃这个属性。


     而若要停止动画,需要用到的函数是$("").stop(true)。如果不停止动画而只是单纯隐藏整个DIV的话,动画会继续播放下去,并持续占用CPU资源,所以紧记一定要在最后写上停止动画的语句。停止舰娘动画的函数如下:


function stopShip() {
    $(ship).stop(true);
}


-----------------13-------------------


*13、总:整体固定在窗口的最底部,而非网页的底部
     要实现这个效果,只需要CSS属性中position定义为fixed就可以了,下面便是个人最上层DIV的写法。有趣的是,如果把这里的CSS属性提取出来单独写成一个CSS的话,上面那两张2760PX的海水就会把窗口给撑大。但若像下面那样把CSS属性写在DIV的STYLE中,则不会有问题。其实个人觉得把窗口撑大才是正确的,但浏览器自动帮我把这个问题给解决了。


<div id="kankore_bath" style="position: fixed; bottom: 0; left: 0; opacity: 1;">
......
</div>


-----------------14-------------------


*14、总:只在BLOG首页显示该特效
     在初始化整个“翻滚吧舰娘!”时写上域名判断就可以了,具体实现代码可见下面。为什么要只在BLOG首页显示?一来是为了保证不影响文章阅读性,二来是为了不那么占用CPU资源。个人的笔记本就是只要一运行该动画特效,CPU风扇就会响得恐怖。所以把其限定在首页上显示就可以了。


$(document).ready(function () {
    if (document.URL == "http://hero32167.blog126.fc2.com/") {
        document.ondragstart = function () { return false; }
        setDirection();
        setShip();
        animateWater();
        animateShip();
    }
    else {
        $("#kankore_bath").css("display", "none");
    }
})


     document.ondragstart = function () { return false; },这句是从萌购源代码抄回来的,作用是:禁止鼠标拖拽图片。但其实个人没看出这句的实际效果,姑且留着作参考吧。


-------------华丽的分割线-------------


     代码解释大概就是这么多了,下面是测试时间,个人特意用了6个浏览器来测试这个特效。可惜公司里的系统不支持IE10,不然就连IE10也可以一起测试了。测试结果如下:


1、FIREFOX:全特效及格
2、IE8:舰娘不会摇晃,海水不会透明,点击版权时整体不会渐变消失
3、IE9:舰娘摇晃时没有动画效果
4、CHROME:鼠标拖动舰娘时有延迟,版权字体偏大
5、OPERA:鼠标拖动舰娘时有延迟,版权字体偏大
6、SAFARI:鼠标在版权变换图示时有延迟,版权字体模糊,整体反应很慢


     IE的问题上面已经解释过了;OPERA和CHROME的表现一样,就是字体和FIREFOX不同,反应也比FIREFOX慢,算是不过不失;至于SAFARI,我只想说这玩意在PC上为什么还有人用,水平实在太低了,各种卡得快崩溃似的,完全不能接受。个人还特意试了IPAD MINI上的SAFARI,除了鼠标事件无效外,其余表现正常,还能看到两张海水图的分割线,算是个意外的BUG。


     测试完自己这只舰娘后,个人又手痒的测试了萌购的那只。由于是作用在商业环境下,所以萌购的舰娘表现效果限制肯定是比个人这只大的。之后的测试也证实了这个看法,结果如下:


1、FIREFOX:全特效及格
2、IE8:舰娘不会摇晃,海水不会流动和透明
3、IE9:舰娘不会摇晃,海水不会流动,舰娘从高处掉落时没有紧急缓冲动画
4、CHROME:鼠标拖动舰娘时有延迟,版权字体偏大,舰娘从高处掉落时没有紧急缓冲动画
5、OPERA:鼠标拖动舰娘时有延迟,版权字体偏大
6、SAFARI:鼠标事件严重影响到背景,版权字体模糊,整体反应很慢,没有掉落动画


     IE9和CHROME没有紧急缓冲动画这个让我有点意外,不知道萌购的代码实现和我的有什么不同,可以让CHROME和OPERA有不同的表现;FIREFOX一如既往的满分表现;IE8还是同样问题;SAFARI又一次被轰杀至渣了……个人又一次拿起了IPAD MINI的SAFARI,除了鼠标事件无效外,萌购这只其余表现同样正常。可惜舰娘和海水出现了位置差,导致两者完全分离,成了水在下面流,舰娘在天上飞的情况……





*



本版积分规则

小黑屋|推特:squid4046 ( 互联网ICP备案:庆丰ICP备8964233号-3 习ICP证:习2B-20100043 |

GMT+8, 2025-10-31 05:55 , Processed in 0.140163 second(s), 42 queries .

Powered by Discuz! X3.5

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表