Vue新手指引

WEB

driver.js

npm install driver.js --save

utils / driver.js

export default [
    {
        element: '#some-element1',
        popover: {
          title: '第一项',
          description: '第一项的描述',
          position: 'bottom',
        }
    },
    {
        element: '#some-element2',
        popover: {
          title: '第二项',
          description: '第二项的描述',
          position: 'top',
        }
    },
]

挂载

import Driver from 'driver.js'
import 'driver.js/dist/driver.min.css'
import Vue from 'vue'
Vue.prototype.$driver = new Driver()
import Driver from 'driver.js';
import 'driver.js/dist/driver.min.css'
import Vue from 'vue'
Vue.prototype.$driver = new Driver({  
    allowClose: false, //禁止点击外部关闭
    doneBtnText: '完成', // 完成按钮标题
    closeBtnText: '关闭', // 关闭按钮标题
    stageBackground: '#fff', // 引导对话的背景色
    nextBtnText: '下一步', // 下一步按钮标题
    prevBtnText: '上一步', // 上一步按钮标题
})

.vue

<div id="some-element1">第一个</div>
<div id="some-element2">第二个</div>
import driverStep from "@/utils/driver"
// 在mounted生命周期或方法中执行下述代码
this.$driver.defineSteps(driverStep)
this.$driver.start()
// 方法
start () {
  this.$nextTick(() => {
    this.$driver.defineSteps(driverStep)
    this.$driver.start()
  })
}

Vue3.0 compositionAPI

<!--
 * @Author: zhang gen yuan
 * @Date: 2021-09-17 11:01:54
 * @Descripttion: 
-->
<template>
  <h1 id="div1">1111111111</h1>
  <h1 id="div2">2222222222</h1>
</template>

<script setup>
import Driver from "driver.js";
import driverStep from "/@/utils/driver";
import { onMounted, nextTick } from "vue";
import { useRouter } from 'vue-router'
let router = useRouter()
let $driver = new Driver({
  allowClose: false, //禁止点击外部关闭
  doneBtnText: "完成", // 完成按钮标题
  closeBtnText: "关闭", // 关闭按钮标题
  stageBackground: "#fff", // 引导对话的背景色
  nextBtnText: "下一步", // 下一步按钮标题
  prevBtnText: "上一步", // 上一步按钮标题
  onHighlightStarted: (Element) => {
    // 监听当前高亮DOM
  },
  onHighlighted: (Element) => {
    // 监听当前高亮DOM
  },     
  onDeselected: (Element) => {
    // 监听关闭按钮
  },
  onReset: (Element) => {
    // 监听完成按钮
  },          
  onNext: (Element) => {
    // 监听下一步
  },           
  onPrevious: (Element) => {
    //监听上一步按钮
  },  
});
onMounted(() => {
  $driver.defineSteps(driverStep);
  $driver.start();
});
</script>

<style>
</style>

uniAPP

index.vue

<template>
	<view class="content">
		<!-- <image class="logo" src="/static/logo.png" id="testid1"></image>
		<view class="text-area" id="testid2">
			<text class="title">Hello</text>
		</view>
		<view id="testid3">测试行1</view>
		<view id="testid4"><button>测试按钮1</button></view> -->
		<view id="testid1">111</view>
		<view id="testid2">222</view>
		<view id="testid3">333</view>
		<view id="testid4">444</view>
		<!-- 新手引导 -->
		<guide :show="showGuide" :width="cWidth" :height="cHeight" :left="cLeft" :top="cTop"
			:showMessage='cShowMsg' :currentIndex="currentIndex" :noticeArray="noticeArray" @click="clicktoNext">
		</guide>
	</view>
</template>

<script>
	import guide from '@/components/guide/guide.vue'
	export default {
		data() {
			return {
				showGuide: true, //引导是否显示 
				cShowMsg: '', // 展示的解释语
				cWidth: '',
				cHeight: '',
				cLeft: '',
				cTop: '',
				currentIndex: 0,
				//配置需要显示引导的view以及引导显示的msg
				noticeArray: [{
						"showID": "testid1", // 对应的id
						"showMessage": "测试文本1", // 对应的解释文本
						"type": "left" // 解释框的气泡类型
					},
					{
						"showID": "testid2",
						"showMessage": "测试文本2",
						"type": "right"
					},
					{
						"showID": "testid3",
						"showMessage": "测试多行文本测试多行文本测试多行文本测试多行文本测试多行文本测试多行文本测试多行文本测试多行文本测试多行文本测试多行文本",
						"type": "top"
					},
					{
						"showID": "testid4",
						"showMessage": "测试文本4",
						"type": "bottom"
					}
				]
			}
		},
		components: {
			guide
		},
		onLoad() {
			let _this = this;
			this.$nextTick(function() {
				if (_this.currentIndex >= _this.noticeArray.length) {
					_this.showGuide = false;
					return;
				}
				_this.showGuide = true;
				_this.cShowMsg = _this.noticeArray[_this.currentIndex].showMessage;
				var idS = '#' + _this.noticeArray[_this.currentIndex].showID;
				console.log(idS)
				//根据布局信息显示引导框位置
				const query = uni.createSelectorQuery().in(_this);
				query.select(idS).boundingClientRect(data => {
					console.log("得到布局位置信息" + JSON.stringify(data));
					_this.cWidth = data.width;
					_this.cHeight = data.height;
					_this.cLeft = data.left;
					_this.cTop = data.top;
				}).exec();
			});
		},
		methods: {
			clicktoNext() {
				if (this.currentIndex >= this.noticeArray.length) {
					this.showGuide = false;
					return;
				}
				this.noticeArray[this.currentIndex].zindex = 0;
				this.cShowMsg = '';
				this.currentIndex++;
				if (this.currentIndex >= this.noticeArray.length) {
					this.showGuide = false;
					return;
				}
				this.cShowMsg = this.noticeArray[this.currentIndex].showMessage;
				var idS = '#' + this.noticeArray[this.currentIndex].showID;
				console.log(idS)
				const query = uni.createSelectorQuery().in(this);
				query.select(idS).boundingClientRect(data => {
					this.cWidth = data.width;
					this.cHeight = data.height;
					this.cLeft = data.left;
					this.cTop = data.top;
				}).exec();

			},
		}
	}
</script>

guide.vue

<!-- guide.vue -->
<template>
	<view v-show="show" class="main" @touchmove.stop>
		<view class="rect-shadow" :style="{'width':width+'px','height':height+'px','left':left+'px',top:top+'px','border-radius': noticeArray[currentIndex].radius?noticeArray[currentIndex].radius+'rpx':'' }"></view>
		<template v-if="noticeArray[currentIndex].type == 'left' ">
			<view  class="show-message_left" :style="{'top': top +'px', 'right': winWidth-right+width+20+'px' }">
				<view style="padding: 12rpx 18rpx;">{{showMessage}}</view>
			</view>			
		</template>
		<template v-if="noticeArray[currentIndex].type == 'top' ">
			<view :class="left<(winWidth/2)?'show-message_top_left':'show-message_top_right'" :style="{'top': (top-height-topMsgHeght) +'px', 'left': left<(winWidth/2)?left+'px':'' , 'right': left>(winWidth/2)?(winWidth-right)+'px':''  }">
				<view style="padding: 12rpx 18rpx;">{{showMessage}}</view>
			</view>			
		</template>
		<template v-if="noticeArray[currentIndex].type == 'right' ">
			<view  class="show-message_right" :style="{'top': top +'px', 'left': (left+width+20) +'px' }">
				<view style="padding: 12rpx 18rpx;">{{showMessage}}</view>
			</view>			
		</template>
		<template v-if="noticeArray[currentIndex].type == 'bottom' ">
			<view :class="left<(winWidth/2)?'show-message_bottom_left':'show-message_bottom_right'" :style="{'top': (top+height+15) +'px', 'left': left<(winWidth/2)?left+'px':'' , 'right': left>(winWidth/2)?(winWidth-right) +'px':'' }">
				<view style="padding: 12rpx 18rpx;">{{showMessage}}</view>
			</view>			
		</template>
		<view class="cover-wrap" >
			<view class="guide-next" @tap="click" :style="{color: currentIndex==noticeArray.length-1? '#EA3E4F':'#000000'}">{{nextText}}</view>
		</view>
	</view>
</template>

<script>
	export default {
		name: "guide",
		props: {
			show: {
				type: Boolean,
				default: false
			},
			left: {
				type: [Number, String],
				default: ''
			},
			right: {
				type: [Number, String],
				default: ''
			},
			top: {
				type: [Number, String],
				default: ''
			},
			width: {
				type: [Number, String],
				default: ''
			},
			height: {
				type: [Number, String],
				default: ''
			},
			showMessage: {
				type: String,
				default: ''
			},
			currentIndex: {
				type: Number,
				default: 0
			},
			noticeArray: {
				type: Array,
				default: []
			}
		},
		data() {
			return {
				topMsgHeght: 0,
				winWidth: 0,
				nextText: '下一步'
			}
		},
		watch: {
			show(n) {},
			currentIndex(value) {
				if (value != this.noticeArray.length&&this.noticeArray[value].type == 'top') {
					const className = this.left<(this.winWidth/2) ? '.show-message_top_left' : '.show-message_top_right'
					const query = uni.createSelectorQuery().in(this);
					query.select(className).boundingClientRect(data => {
						this.topMsgHeght = data.height - 29
					}).exec();
				}
				if (value == this.noticeArray.length-1) {
					this.nextText = '知道了,关闭说明'
				}
			}
		},
		methods: {
			click() {
				this.$emit('click');
			}
		},
		created() {
			setTimeout(() => {
				uni.getSystemInfo({
					success: (res) => {
						this.winWidth = res.windowWidth;
					}
				});	
			}, 60)
		}
	}
</script>

<style lang="scss" scoped>
	.main {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 99999;
	}
	.rect-shadow {
		position: absolute;
		border-radius: 12rpx;
		box-shadow: 0 0 0 3000px rgba(0, 0, 0, 0.5);
		pointer-events: none;
	}
	.show-message_left,
	.show-message_top_left,
	.show-message_top_right,
	.show-message_right,
	.show-message_bottom_left,
	.show-message_bottom_right {
		position: absolute;
		border-radius: 12rpx;
		background-color: rgba(0,0,0,.5);
		text-align: center;
		pointer-events: none;
		color: #fff;
		font-size: 24rpx;
		max-width: 50%;
		text-align: left;
	}
	.show-message_left::after {
		content: '';
		border-left: 12rpx solid rgba(0,0,0,.5);
		border-top: 12rpx solid transparent;
		border-right: 12rpx solid transparent;
		border-bottom: 12rpx solid transparent;
		width: 0;
		height: 0;
		position: absolute;
		top: 25%;
		right: -22rpx;
	}
	
	.show-message_top_left::after,.show-message_top_right::after {
		content: '';
		border-left: 12rpx solid transparent;
		border-top: 12rpx solid rgba(0,0,0,.5);
		border-right: 12rpx solid transparent;
		border-bottom: 12rpx solid transparent;
		width: 0;
		height: 0;
		position: absolute;
		top: 100%;
	}
	.show-message_top_left::after {
		left: 20%;
	}
	.show-message_top_right::after {
		right: 20%;
	}
	
	.show-message_right::after {
		content: '';
		border-left: 12rpx solid transparent;
		border-top: 12rpx solid transparent;
		border-right: 12rpx solid rgba(0,0,0,.5);
		border-bottom: 12rpx solid transparent;
		width: 0;
		height: 0;
		position: absolute;
		top: 25%;
		left: -24rpx;
	}
	.show-message_bottom_left::after, .show-message_bottom_right::after {
		content: '';
		border-left: 12rpx solid transparent;
		border-top: 12rpx solid transparent;
		border-right: 12rpx solid transparent;
		border-bottom: 12rpx solid rgba(0,0,0,.5);
		width: 0;
		height: 0;
		position: absolute;
		top: -22rpx;
	}
	
	.show-message_bottom_left::after {
		left: 20%;
	} 
	.show-message_bottom_right::after {
		right: 20%;
	}
	
	.cover-wrap {
		width: 100vw;
		height: 100vh;
		top: 0;
		left: 0;
		position: absolute;
		
		.guide-next {
			background-color: #FFFFFF;
			font-size: 28rpx;
			border-radius: 38rpx;
			position: absolute;
			bottom: 372rpx;
			left: 50%;
			transform: translate(-50%,0);
			padding: 16rpx 32rpx;
		}
	}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值