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>

浙公网安备 33010602011771号