乐趣区

关于html5:网易云音乐年度歌单的卡通形象联动制作

最近朋友圈被很多网易云音乐的年底歌单给刷屏了, 我也去看了我的年度歌单, 发现一个有意思的交互成果, 抉择卡通形象, 通过滑动抉择人物的不同头像, 衣服, 裤子 最终塑造成一个领有独立共性的卡通形象.

界面成果预览

交互成果预览

制作素材

把每个滑动的图片进行了全屏截图, 而后通过图片解决工具去除背景, 制作成对立大小的 png 图片.



图片的卡通元素都是通过截图获取, 每个元素被解决成对立大小, 局部会有锯齿, 仅供参考. 这里头部比拟非凡, 每个形象的头部大小不一, 这里取一个对立的截止线, 不便前面整合成整个形象. 其它相似, 顶对齐即可.

剖析交互的特点

1. 轮播图
2. 跨屏
3. 滑动循环
4. 局部衣服滑动会触发裤子的扭转
5. 局部裤子滑动会触发衣服的扭转
6. ...


轮播图代码

<div id="slide" class="bui-slide bui-slide-skin01"></div>
var uiSlide = bui.slide({
        id: "#slide",
        height: 320,
        // autopage: true, // 主动分页
        data: [{
            image: "images/banner01.png",
            url: "pages/ui_controls/bui.slide_title.html",
        }, {
            image: "images/banner02.png",
            url: "pages/ui_controls/bui.slide_title.html",
        }, {
            image: "images/banner03.png",
            url: "pages/ui_controls/bui.slide_title.html",
        }],
        loop: true, // 循环
    })

跨屏轮播图只需加上 cross:true 参数即可. 相熟 BUI 的敌人, 一眼就能找到相似的成果, 跨屏轮播图 第 1 - 第 3 的特点就解决了.

有意思的是第 4 点第 5 点, 轮播图切换的时候局部须要互相关联.

实现的外围思路:

  1. 页面有一个动态全屏轮播图, 用于点击下一步, 上一步的整屏切换. 动态轮播图的益处是构造能够自定义.
  2. 首屏初始化三个跨屏轮播图, 用于头部, 衣服, 裤子的失常抉择切换;
  3. 点击轮播图的时候, 切换激活状态, 非激活状态暗藏左右两个图片 (暗藏通过 css), 并禁止滑动 ;
  4. 当滑动选中当前, 别离把头部, 衣服, 裤子的图片地址, 索引 缓存在 bui.store (轮播图的 to 回调外面);
  5. 通过 bui.store 创立衣服跟裤子的关联 conection 字段, 当检测到滑动的图片有配套裤子的时候, 主动滑动下一个轮播图到指定地位;
  6. 点击下一步去到第 2 屏, 用于展现刚刚选中的数据;
// 衣服
const cartoonBody = bui.slide({
    id: "#cartoonBody",
    height: 320,
    stopPropagation: false,
    autopage: false,
    cross: true,
    loop: true,
    data: this.$data.cartoon.body
}).on("to", function () {let index = this.index();
    // bui.store 读取的时候须要应用 this.$data.xxx , 如果应用 this.xxx 读取会导致最终的值不能设置正确.
    let img = that.$data.cartoon.body[index].image;
    // 设置
    that.profile.body.image = img;
    that.profile.body.index = index;

    // 检测衣服跟裤子的关系索引
    let item = bui.array.get(that.$data.conection, img, "body");
    let footindex = bui.array.index(that.$data.cartoon.foot, item.foot, "image");

    if (footindex >= 0 && that.$data.active[1] == "active-block") {
        // 操作裤子的实例, 跳转的时候, 因为 loop:true, 这里的索引须要在实在的索引下 +1 
        that.$data.distances[2].to(footindex + 1, "none")
    }

}).lock();// lock 禁止滑动
// 裤子
const cartoonFoot = bui.slide({
    id: "#cartoonFoot",
    height: 320,
    stopPropagation: false,
    autopage: false,
    cross: true,
    loop: true,
    data: this.$data.cartoon.foot
}).on("to", function () {let index = this.index();
    let img = that.$data.cartoon.foot[index].image;
    that.profile.foot.image = img;
    that.profile.foot.index = index;

    // 检测衣服跟裤子的关系索引
    let item = bui.array.get(that.$data.conection, img, "foot");
    let bodyindex = bui.array.index(that.$data.cartoon.body, item.body, "image");
    if (bodyindex >= 0 && that.$data.active[2] == "active-block") {
        // 操作衣服的实例, 跳转的时候, 因为 loop:true, 这里的索引须要在实在的索引下 +1 
        that.$data.distances[1].to(bodyindex + 1, "none")
    }
}).lock();// lock 禁止滑动 

最终成果


github 地址: https://github.com/imouou/BUI…

codepen 地址: https://codepen.io/imouou/ful…

BUI 专一挪动开发, 灵便超出你的设想, 感谢您的浏览.

多页残缺代码

<!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <title>BUI</title>
    <meta name="format-detection" content="telephone=no" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/buijs@latest/lib/latest/bui.css" />
    
<style>
    .cartoon-page main,
    .step-item {
        background-color: #f2c9bc;
        padding-top: .2rem;
    }
    .step-item {
        width: 100%;
        height: 100%;
    }
    .cartoon-page h1,
    .cartoon-page p {
        text-align: center;
        color: #675553;
    }
    .cartoon-wrap .bui-slide {margin-bottom: .2rem;}
    .cartoon-wrap .bui-slide-img{
        width: 4rem;
        height: 3.2rem;
        background-color: #e2b4a3;
        border-radius: .2rem;
    }
    .cartoon-wrap .active-block .bui-slide-img{background-color: #fff;}
    .cartoon-wrap .active-block .bui-cross-prev,
    .cartoon-wrap .active-block .bui-cross-next{visibility: visible;}
    .cartoon-wrap  .bui-cross-prev,
    .cartoon-wrap  .bui-cross-next{visibility: hidden;}
    .cartoon-wrap  .bui-cross-prev .bui-slide-img,
    .cartoon-wrap  .bui-cross-next .bui-slide-img{background-color: rgba(255,255,255,.3);
    }
    .bui-btn-step {
        width: 1.4rem;
        height: 1.4rem;
        line-height: 1.4rem;
        color: #fff;
        background-color: #f5433b;
        border: 3px solid rgba(255,255,255,0.8);
        padding: 0;
        margin-bottom: .2rem;
    }
    .bui-slide-cross .bui-cross-next .bui-slide-img, 
    .bui-slide-cross .li-next .bui-slide-img{margin-left: 0;}
    .bui-slide-cross .bui-cross-prev .bui-slide-img, 
    .bui-slide-cross .li-prev .bui-slide-img{margin-right: 0;}
    .bui-slide-fullscreen>.bui-slide-main>ul>li img.cartoonhead ,
    .bui-slide-fullscreen>.bui-slide-main>ul>li img.cartoonbody,
    .bui-slide-fullscreen>.bui-slide-main>ul>li img.cartoonfoot {
        display: block;
        width:3.2rem ;
        height:3.2rem ;
    }
    .cartoonhead {
        position: relative;
        z-index: 3;
    }
    .cartoonbody {
        margin-top: -1.1rem;
        position: relative;
        z-index: 2;
    }
    .cartoonfoot {
        margin-top: -1.1rem;
        position: relative;
        z-index: 1;
    }
</style>
</head>
<body>
<!-- HTML Begin-->

<!-- 这里还是一个规范的 BUI 页面 -->
<div class="bui-page bui-box-vertical cartoon-page">
    <header></header>
    <main>
        <!-- 动态轮播图 -->
        <div id="uiSlide" class="bui-slide">
            <div class="bui-slide-main">
                <ul>
                    <li>
                        <!-- 垂直布局 -->
                        <div class="step-item bui-box-center bui-box-vertical fullheight">

                            <div class="span1">
                                <h1> 设置形象, 开启年度报告 </h1>
                                <p> 左右切换抉择造型 </p>
                                <div class="bui-box bui-box-vertical cartoon-wrap">
                                    <div class="span1" b-class="cartoons.active.0" b-click="cartoons.activeBlock(0)">
                                        <div id="cartoonHead" class="bui-slide"></div>
                                    </div>
                                    <div class="span1" b-class="cartoons.active.1" b-click="cartoons.activeBlock(1)">
                                        <div id="cartoonBody" class="bui-slide"></div>
                                    </div>
                                    <div class="span1" b-class="cartoons.active.2" b-click="cartoons.activeBlock(2)">
                                        <div id="cartoonFoot" class="bui-slide"></div>
                                    </div>
                                    <!-- <div class="span1" b-class="cartoons.active.3" b-click="cartoons.activeBlock(3)">
                                        <div id="cartoonDeco" class="bui-slide"></div>
                                    </div> -->
                                </div>
                            </div>
                            <div class="container-y">
                                <div class="bui-btn-step ring" b-click="cartoons.next"> 下一步 </div>
                            </div>
                        </div>
                    </li>
                    <li style="display:none;">
                        <!-- 垂直布局 -->
                        <div class="step-item bui-box-center bui-box-vertical fullheight">
                            <!-- 最终形象 -->
                            <div class="span1">
                                <div class="bui-box-center">
                                    <div class="wrap-img">
                                        ![](cartoons.profile.head.image)
                                        ![](cartoons.profile.body.image)
                                        ![](cartoons.profile.foot.image)
                                    </div>
                                </div>
                            </div>
                            <div class="container-y">
                                <div class="bui-btn-step ring" b-click="cartoons.prev"> 上一步 </div>
                            </div>
                        </div>
                    </li>
                </ul>
            </div>
        </div>
    </main>
</div>
<!-- HTML End-->
    <!-- 依赖库 手机调试的 js 援用程序如下 -->
    <script src="https://cdn.jsdelivr.net/npm/buijs@latest/lib/zepto.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/buijs@latest/lib/latest/bui.js"></script>
    <script>
        bui.ready(function () {
            // 这里写业务及控件初始化, 一个页面只能有一个 bui.ready
            // 页面跳转的全屏轮播图
            const uiSlideStep = bui.slide({
                id: "#uiSlide",
                autopage: false,
                fullscreen: true,
                swipe: false,
                loop: false
            })
            // 初始化数据行为存储
            const bs = bui.store({
                el: `.bui-page`,
                scope: "cartoons",
                data: {
                    // 衣服裤子的关系, 局部衣服关联裤子, 裤子关联衣服
                    conection: [{
                        body: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body02.png",
                        foot: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot01.png"
                    }, {
                        body: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body03.png",
                        foot: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot05.png"
                    }, {
                        body: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body12.png",
                        foot: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot08.png"
                    }, {
                        body: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body13.png",
                        foot: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot07.png"
                    }, {
                        body: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body14.png",
                        foot: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot06.png"
                    }],
                    distances: [], // 存储滑动的实例
                    active: {
                        0: "active-block",
                        1: "",
                        2: "",
                    },
                    profile: {
                        // 集体形象的存储
                        head: {
                            image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head01.png",
                            index: 0,
                        },
                        body: {
                            image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body01.png",
                            index: 0,
                        },
                        foot: {
                            image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot01.png",
                            index: 0,
                        },
                        deco: {
                            image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/deco/deco01.png",
                            index: 0,
                        }
                    },
                    cartoon: {
                        active: 0, // 激活的 slide, 默认头部
                        head: [{image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head01.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head02.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head03.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head04.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head05.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head06.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head07.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head08.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head09.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head10.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head11.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/head/head12.png",}],
                        body: [{image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body01.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body02.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body03.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body04.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body05.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body06.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body07.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body08.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body09.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body10.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body11.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body12.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body13.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/body/body14.png",}],
                        foot: [{image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot01.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot02.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot03.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot04.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot05.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot06.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot07.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot08.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/foot/foot09.png",}],
                        deco: [{image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/deco/deco01.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/deco/deco02.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/deco/deco03.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/deco/deco04.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/deco/deco05.png",}, {image: "https://gitee.com/imouou/bui-case-cartoon/raw/main/src/images/cartoon/deco/deco06.png",}],
                    },
                },
                methods: {activeBlock(index) {for (let i = 0; i < Object.keys(this.$data.active).length; i++) {this.active[i] = "";
                            this.$data.distances[i].lock();}
                        // 给激活的滑动图加上款式, 区别其它两个
                        this.active[index] = "active-block";
                        this.$data.distances[index].unlock();},
                    next() {uiSlideStep.next();
                    },
                    prev() {uiSlideStep.prev();
                    }
                },
                mounted: function () {
                    // 焦点图 js 初始化:
                    let that = this;
                    const cartoonHead = bui.slide({
                        id: "#cartoonHead",
                        height: 320,
                        autopage: false,
                        stopPropagation: false,
                        cross: true,
                        loop: true,
                        data: this.$data.cartoon.head
                    }).on("to", function () {let index = this.index();
                        // bui.store 读取的时候须要应用 this.$data.xxx , 如果应用 this.xxx 读取会导致最终的值不能设置正确.
                        let img = that.$data.cartoon.head[index].image;
                        // 设置
                        that.profile.head.index = index;
                        that.profile.head.image = img;

                    })

                    const cartoonBody = bui.slide({
                        id: "#cartoonBody",
                        height: 320,
                        stopPropagation: false,
                        autopage: false,
                        cross: true,
                        loop: true,
                        data: this.$data.cartoon.body
                    }).on("to", function () {let index = this.index();
                        // bui.store 读取的时候须要应用 this.$data.xxx , 如果应用 this.xxx 读取会导致最终的值不能设置正确.
                        let img = that.$data.cartoon.body[index].image;
                        // 设置
                        that.profile.body.image = img;
                        that.profile.body.index = index;

                        // 检测衣服跟裤子的关系索引
                        let item = bui.array.get(that.$data.conection, img, "body");
                        let footindex = bui.array.index(that.$data.cartoon.foot, item.foot, "image");

                        if (footindex >= 0 && that.$data.active[1] == "active-block") {
                            // 操作裤子的实例, 跳转的时候, 因为 loop:true, 这里的索引须要在实在的索引下 +1 
                            that.$data.distances[2].to(footindex + 1, "none")
                        }

                    }).lock();

                    const cartoonFoot = bui.slide({
                        id: "#cartoonFoot",
                        height: 320,
                        stopPropagation: false,
                        autopage: false,
                        cross: true,
                        loop: true,
                        data: this.$data.cartoon.foot
                    }).on("to", function () {let index = this.index();
                        let img = that.$data.cartoon.foot[index].image;
                        that.profile.foot.image = img;
                        that.profile.foot.index = index;

                        // 检测衣服跟裤子的关系索引
                        let item = bui.array.get(that.$data.conection, img, "foot");
                        let bodyindex = bui.array.index(that.$data.cartoon.body, item.body, "image");
                        if (bodyindex >= 0 && that.$data.active[2] == "active-block") {
                            // 操作衣服的实例, 跳转的时候, 因为 loop:true, 这里的索引须要在实在的索引下 +1 
                            that.$data.distances[1].to(bodyindex + 1, "none")
                        }
                    }).lock();

                    // const cartoonDeco = bui.slide({
                    //     id: "#cartoonDeco",
                    //     height: 320,
                    //     stopPropagation: false,
                    //     autopage: false,
                    //     cross: true,
                    //     loop: true,
                    //     data: this.$data.cartoon.deco
                    // }).on("to", function () {//     let index = this.index();

                    //     that.profile.deco.image = that.$data.cartoon.deco[index].image
                    //     that.profile.deco.index = index;
                    // }).to(0, "none").lock();

                    // 增加实例, 跟 cartoon.active 的数值对应.
                    this.distances.push(cartoonHead, cartoonBody, cartoonFoot);

                }
            })
        })
    </script>
</body>
</html>
退出移动版