uniapp 时间轴
先上图看图说话
时间轴的轴线可以居中分两边,也可以像上图一样。
上代码:
<view class="content"> <time-line ref="timeline" location="left"></time-line> </view> <script> import timeLine from '../../components/xuan-timeLine/xuan-timeLine.vue' export default { components:{ timeLine }, data() { return { time:0, isclick:true, }; }, onLoad() { }, onPageScroll() { if(this.isclick){ this.timer(); this.$refs.timeline.getScroll(); } }, onReady() { }, methods: { timer(){ if(this.time>0){ this.isclick=false; this.time--; setTimeout(this.timer,1) } else{ this.isclick=true; this.time=10 } } } }; </script>
xuan-timeLine.vue代码
<template> <scroll-view class="time-line-wrap"> <view class="time-line"> <!-- 时间轴 --> <view class="time-line-container" :class="addTypeClass"> <!-- 时间轴内容块列表 --> <view class="time-line-list"> <!-- 时间轴内容块 --> <view class="time-line-info" :key="index" :class="[layoutClass(index)]" :id="'timeline'+index" v-for="(item,index) of time_line_list"> <!-- 内容块内容 --> <view class="line-info-content" > <!-- 时间轴圆点 --> <view class="line-on-round" :style="{ opacity: current[index]&¤t[index].is=='ok'?1:0,top:'12rpx'}" :class="current[index]&¤t[index].is=='ok'?comeani:''"></view> <view class="info-content-wrap" :style="{ opacity: current[index]&¤t[index].is=='ok'?1:0}" :class="current[index]&¤t[index].is=='ok'?comeani:''"> <!-- 标题 --> <view class="info-title"> {{item.title}} </view> <view class="bmjg">报名<text>2</text>个培训机构,<text>4</text>节培训课程</view> <!-- 内容 --> <view class="info-content"> <view class="jg_bg"> <view class=""> <view class="jg_name">思语校外培训学校</view> <view class="jg_pf_ico"> <image src="../../static/index/pfxx.png" mode=""></image> <image src="../../static/index/pfxx.png" mode=""></image> <image src="../../static/index/pfxx.png" mode=""></image> <image src="../../static/index/pfxx.png" mode=""></image> <image src="../../static/index/pfwxz.png" mode=""></image> <view class="rzbm">4星·成教部门认证</view> </view> </view> <view class=""> <view class="jg_num">8.5</view> <view class="jg_pftxt">评分</view> </view> </view> <view class="kc_bg"> <image class="kc_logo" src="../../static/index/xsfmlist2@2x.png" mode=""></image> <view class=""> <view class="kc_name">初中年级户外研学</view> <view class="kc_time">6月14日-6月20日 · 共三章</view> <view class="jiaoyu">中学教育 · 历史</view> </view> </view> <view class="kc_bg"> <image class="kc_logo" src="../../static/index/xsfmlist2@2x.png" mode=""></image> <view class=""> <view class="kc_name">初中年级户外研学</view> <view class="kc_time">6月14日-6月20日 · 共三章</view> <view class="jiaoyu">中学教育 · 历史</view> </view> </view> </view> <view class="info-content"> <!-- 内容 --> <view class="jg_bg"> <view class=""> <view class="jg_name">思语校外培训学校</view> <view class="jg_pf_ico"> <image src="../../static/index/pfxx.png" mode=""></image> <image src="../../static/index/pfxx.png" mode=""></image> <image src="../../static/index/pfxx.png" mode=""></image> <image src="../../static/index/pfxx.png" mode=""></image> <image src="../../static/index/pfwxz.png" mode=""></image> <view class="rzbm">4星·成教部门认证</view> </view> </view> <view class=""> <view class="jg_num">8.5</view> <view class="jg_pftxt">评分</view> </view> </view> <view class="kc_bg"> <image class="kc_logo" src="../../static/index/xsfmlist2@2x.png" mode=""></image> <view class=""> <view class="kc_name">初中年级户外研学</view> <view class="kc_time">6月14日-6月20日 · 共三章</view> <view class="jiaoyu">中学教育 · 历史</view> </view> </view> <view class="kc_bg"> <image class="kc_logo" src="../../static/index/xsfmlist2@2x.png" mode=""></image> <view class=""> <view class="kc_name">初中年级户外研学</view> <view class="kc_time">6月14日-6月20日 · 共三章</view> <view class="jiaoyu">中学教育 · 历史</view> </view> </view> </view> </view> </view> </view> </view> </view> </view> </scroll-view> </template> <script> export default{ data(){ return{ // 数据 time_line_list:[ { title:'6月', }, { title:'2020年12月', }, ], HEIGHT:0,//屏幕高度 result:[],//监听的结果 current:[],//当前在屏幕内的 sum:0,//加载完成个数 comeani:'come-ani',//入场动画 isScroll:true,//是否加载动画 } }, props:{ location:{ type:String, default:'center', }, title:{ type:String, default:'时间轴' }, }, computed:{ addTypeClass(){ let _class=""; let arrType =["left","right","center"]; if (arrType.indexOf(this.location)!==-1&&this.location!=='center') { _class+=this.location+"-time-line"; } return _class; } }, mounted() { this.init(); }, methods:{ init(){ try { // 获取屏幕高度 const res = uni.getSystemInfoSync(); this.HEIGHT=res.screenHeight; // 添加标志位 for(let i=0;i<this.time_line_list.length;i++){ this.current.push({tag:'timeline'+i,is:'no'}); } // 开始获取位置信息 this.getScroll(); } catch (e) { // error } }, loadani(){ for(let i=0;i<this.result.length;i++){ for(let j=0;j<this.result[i].info.length;j++){ // 是否没加载动画 if(this.current[j].is!='ok'){ // 是否进入视野 if(this.current[j].tag==this.result[i].info[j].tag && this.result[i].info[j].domTop+90<this.HEIGHT){ // 加载动画 this.current[j].is='ok'; this.sum=j+1; } } } // 移除当前 this.result.splice(i,1); } // 是否全部加载完成 if(this.sum==this.time_line_list.length){this.isScroll=false;} }, async getScroll(){ if(!this.isScroll){return;} let info=[]; // 返回位置信息加入数组 for(let i=0;i<this.time_line_list.length;i++){ await this.getNodeList('timeline'+i,i).then(res => { info.push(res); }); } this.result.push({info:info}); // 加载动画 this.loadani(); }, getNodeList(id,i){ // 获取位置信息并返回 return new Promise(resolve=>{ const query = uni.createSelectorQuery().in(this); query.select('#'+id).boundingClientRect(data => { // console.log("得到布局位置信息" + JSON.stringify(data)); // console.log("节点离页面顶部的距离为" + data.top); resolve({domInfo:data.height,domTop:data.top,tag:id}) }).exec(); }); }, // 添加动画 layoutClass(index){ let _class=""; if(this.location=='center'&&index%2!=0){ _class='right-info'; } return _class; } }, } </script> style <style lang="scss"> // .bmjg{ font-size: 24rpx; color: #757575; margin-top: 20rpx; } .bmjg>text{ color: #E18326; } .jg_bg{ display: flex; align-items: center; justify-content: space-between; height: 149rpx; // border-bottom: 2rpx solid #EAEAEA; } .jg_name{ font-size: 28rpx; color: #323232; font-weight: bold; } .jg_num{ font-size: 36rpx; color: #E18326; font-weight: bold; } .jg_pftxt{ font-size: 20rpx; color: #757575; text-align: center; margin-top: 10rpx; } .jg_pf_ico{ display: flex; align-items: center; margin-top: 20rpx; } .jg_pf_ico>image{ width: 24rpx; height: 24rpx; margin-right: 4rpx; } .rzbm{ font-size: 20rpx; color: #E18326; } .kc_bg{ height: 180rpx; border-top: 2rpx solid #EAEAEA; display: flex; align-items: center; } .kc_logo{ width: 174rpx; height: 116rpx; border-radius: 8rpx; margin-right: 20rpx; } .kc_name{ font-size: 28rpx; color: #323232; font-weight: bold; } .kc_time{ font-size: 20rpx; color: #323232; margin: 14rpx 0; } .jiaoyu{ font-size: 20rpx; color: #757575; } // .time-line-wrap{ width: 100%; overflow-x: hidden; } .time-line-wrap{ font-family: sans-serif; .time-line{ padding: 4% 1%; .time-line-title{ font-size: 25rpx; font-weight: 800; text-align: center; margin-bottom: 15rpx; } // 中间的轴 .time-line-container{ padding: 3% 1%; position: relative; &::before{ content: ''; position: absolute; top: 0; left: 0; right: 0; margin: 0 auto; height: 100%; width: 4rpx; background: #F0F0F0; } .time-line-list{ width: 100%; .time-line-info{ position: relative; .line-info-content{ will-change: auto; width: 43%; display: flex; flex-direction: column; .line-on-round{ height: 20rpx; width: 20rpx; border: 4rpx solid #E18427; border-radius: 50%; background: #fff; position: absolute; left: 0; right: 0; margin: 0 auto; top: 50rpx; box-shadow: 0rpx 2rpx 1rpx 1rpx rgba(0,0,0,.1); z-index: 10; &.come-ani{ animation: come-round .8s ease-in-out; } } .info-content-wrap{ position: relative; .info-title{ min-height: 30rpx; word-break: break-word; text-align: right; margin: 5rpx 0; font-size: 34rrpx; font-weight: 500; color: #E18427; font-weight: bold; } .info-content{ box-shadow: 1rpx 1rpx 1rpx 1rpx #d7e4ed; border-radius: 8rpx; padding: 30rpx; min-height: 40rpx; position: relative; margin-top: 20rpx; .info-txt{} // &::before{ // content: ''; // border: 7rpx solid; // border-color: transparent transparent transparent #aaa; // position: absolute; // left: 100%; // top: 18rpx; // } } &.come-ani{ animation: come-in-left .6s ease-in-out; } } } } .right-info{ transform: rotateY(180deg); .line-info-content{ .info-content-wrap{ .info-title{ transform: rotateY(180deg); text-align: left; } .info-content{ .info-txt{transform: rotateY(180deg)} } } } } } } // 轴在左边 .left-time-line{ &::before{ margin: 0; left: 15rpx; } .time-line-list{ .time-line-info{ .line-info-content{ width: auto; .line-on-round{ left: -4rpx; margin: 0; } .info-content-wrap{ margin-left: 45rpx; margin-bottom: 15rpx; .info-title{ text-align: left; } .info-content{ &::before{ border-color: transparent #aaa transparent transparent; left: -15rpx; } } &.come-ani{ animation: come-in-right .6s ease-in-out; } } } } } } // 轴在右边 .right-time-line{ &::before{ margin: 0; left: 92.8%; } .time-line-list{ .time-line-info{ .line-info-content{ width: auto; .line-on-round{ left: 90%; margin: 0; } .info-content-wrap{ margin-right: 55rpx; margin-bottom: 15rpx; .info-content{ &::before{ border-color: transparent transparent transparent #aaa; } } } } } } } } } @keyframes come-in-left { 0% { transform: translateX(-120%); } 70% { transform: translateX(8%); } 100% { transform: translateX(0); } } @keyframes come-in-right { 0% { transform: translateX(120%); } 70% { transform: translateX(-8%); } 100% { transform: translateX(0); } } @keyframes come-round { 0% { transform: scale(0); opacity: 0; } 40% { opacity: 0; } 60% { transform: scale(1.2); } 100% { transform: scale(1); } } </style>