1

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

界面效果预览

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>

王小o
312 声望61 粉丝

BUI专注webapp快速开发