最近朋友圈被很多网易云音乐的年底歌单给刷屏了, 我也去看了我的年度歌单, 发现一个有意思的交互效果, 选择卡通形象, 通过滑动选择人物的不同头像,衣服,裤子 最终塑造成一个拥有独立个性的卡通形象.
界面效果预览
交互效果预览
制作素材
把每个滑动的图片进行了全屏截图, 然后通过图片处理工具去除背景, 制作成统一大小的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点, 轮播图切换的时候部分需要相互关联.
实现的核心思路:
- 页面有一个静态全屏轮播图, 用于点击下一步,上一步的整屏切换. 静态轮播图的好处是结构可以自定义.
- 首屏初始化三个跨屏轮播图, 用于头部,衣服,裤子的正常选择切换;
- 点击轮播图的时候, 切换激活状态, 非激活状态隐藏左右两个图片(隐藏通过css), 并禁止滑动 ;
- 当滑动选中以后,分别把头部,衣服,裤子的图片地址,索引 缓存在 bui.store (轮播图的to回调里面);
- 通过bui.store 创建衣服跟裤子的关联 conection 字段, 当检测到滑动的图片有配套裤子的时候,自动滑动下一个轮播图到指定位置;
- 点击下一步去到第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>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。