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

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

界面效果预览

image

交互效果预览

image

image

制作素材

把每个滑动的图片进行了全屏截图, 然后通过图片处理工具去除背景, 制作成统一大小的png图片.

image
image
image

图片的卡通元素都是通过截图获取, 每个元素被处理成统一大小, 部分会有锯齿, 仅供参考. 这里头部比较特殊, 每个形象的头部大小不一, 这里取一个统一的截止线, 方便后面整合成整个形象. 其它类似,顶对齐即可.

分析交互的特点

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

image
轮播图代码

<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, // 循环
    })

image

跨屏轮播图只需加上 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禁止滑动

最终效果

image
image

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

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

BUI专注移动开发, 灵活超出你的想象, 感谢您的阅读.

image.png

多页完整代码

<!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>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

owilson

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值