提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
系统大概构成:vite + vue + Mars3d/cesium(地图)
在系统基本完成之后,甲方爸爸突然要求上一个轮播的版本,大概类似于PPT播放的翻页效果,下面是一些尝试方案和最终解决办法。
一、使用router.push()实现页面切换,加入页面过渡动效实现切换展示
一开始的想法是通过router.push()跳转,设置定时器实现页面切换,加入过渡动效,即使用 <transition >
就可以实现页面切换的效果。
想法很美好,正常情况也是可以实现这个需求的,但是我们系统上多数的页面都需要地图展示效果,为了防止过多的加载情况,系统是公用地图组件,切换页面时仅仅对地图矢量进行更新,这就导致切换页面时,地图并不会跟随页面切换过渡动效变化,而会停留在页面中心,所以最终弃用了这个方案。
如果没有类似这种情况,只是单纯的页面切换轮播,感觉切换路由也是种不错的选择。
代码如下:
<div class="app-root" @touchstart="handler" @touchend="onLeave" @mouseenter="onEnter" @mouseleave="onleave" :key="key">
<!-- 地图部分 -->
<MapContainer />
<!-- 页面展示部分 -->
<router-view v-slot="{ Component, route }">
<transition name="slide">
<component :is="Component" :key="route.path”/>
</transition>
</router-view>
</div>
添加定时器和鼠标移入移出事件,在用户使用时控制页面切换的暂停。
const state = reactive({
timer: null, // 定时器
currentPage: 0, // 当前页码
})
function swipe() {
console.log("第n次执行, state.currentPage);
// 这里我把需要轮播的路由写成了静态数据文件,放在routerConfig里面引入
if (state.currentPage < routerConfig.length - 1) {
router.push(routerConfig[state.currentpage + 1]?.path);
state.currentPage++;
} else {
state.currentPage = 0;
router.push(routerConfig[0]?.path)
key.value = key.value + 1;
}
}
// 添加轮播定时器
const setTimer = () => {
if (!state.timer) {
console.log("执行--添加轮播定时器 );
state.timer = setInterval(swipe, swipe_speed);
}
}
// 清除轮播定时器
const clear = () => {
if (state.timer) {
console.log("执行--清除轮播定时器 );
clearInterval(state.timer);
state.timer = null;
}
}
// 鼠标移入移出控制轮播的暂停和继续
const handler = () => {
clear();
}
const onEnter = () => {
clear();
}
const onLeave = () => {
setTimer();
}
onMounted(() => {
setTimer();
})
onUnmounted(() => {
clear();
}
过渡样式
.slide-enter-active,
.slide-leave-active {
transition: all 0.75s ease-out;
}
.slide-enter-to {
position: absolute;
right: 0;
}
.slide-enter-from {
position: absolute;
right: -100%;
}
.slide-leave-to {
position: absolute;
left:-100%;
}
.slide-leave-from {
position: absolute;
left: 0;
}
过渡动效参考:https://router.vuejs.org/zh/guide/advanced/transitions.html
二、使用swiper轮播组件,配合iframe嵌入页面实现轮播效果
1.单纯使用iframe进行页面轮播
为了让页面整体轮播效果更好,更换使用了swiper组件,包裹iframe展示页面,实现走马灯的效果,这一次动画效果是没问题了,但是新的问题又又又出现了,就是它真的太卡了,浏览器渲染占用内存太多(?)很容易就会显示浏览器奔溃了!【我也很奔溃.jpg】
翻阅各种大佬的解决方案后,得出结论,好像解决不了(?),最好就关闭页签再次开始,所以只能另寻他法。
代码如下:
<div class="swipe-root-container" key="swipe-root-container">
<swiper-container
key="swipe-root-container-1"
class="swiper-con"
slides-per-view="1"
:autoplay="{delay: 15000, disableOnInteraction: false, pauseOnMouseEnter: true }"
:centered-slides="true"
:virtual="{ enabled: true }"
:lazy="true"
@progress="onProgress"
@slidechange="onSlideChange"
>
<swiper-slide class="swipers" v-for="(item, index) in arr" :key="index :id="`swiper-class-$(index)`">
<iframe loading="lazy":src="`${apiBase)/#${item.path}?innerObj=${index}`" frameborder="0" @onLoad="onLoad":class="`class-${index}`"></iframe>
</swiper-slide>
</swiper-container>
</div>
swiper 配置参考文档:https://swiperjs.com/element
2.使用 html2canvas ,实现页面截图,后续轮播使用图片轮播
多页面+地图加载渲染会有问题?最后解决方案就是页面只轮播一遍,使用html2canvas将第一遍轮播时的页面生成图片存起来,后续都使用图片轮播;另外为了减少消耗,还添加了切换页面销毁前一个iframe等配置,也算是解决了页面奔溃的问题吧(勉勉强强)。
轮播页面生成截图,代码如下:
import html2canvas from 'html2canvas'
const state= reactive({
flag: 1,
timer1: null,
timer2: null,
imageList:[],
})
const htmlToCanvas = (path, index, name) => {
// 获取iframe内的dom内容
let d = document.querySelector(`.class-${index}`)?.contentwindow;
const iframeBody = d.document.getElementsByTagName('body')[0];
html2canvas(iframeBody,
allowTaint: true,
useCORs: true,backgroundColor: null,
scrolly: 0,
scrollx: 0,
width: window.screen.clientwidth, //dom 原始宽度
height: window.screen.clientHeight,
scale: 2,// 处理模糊问题
dpi: 300,// 处理模糊问题
logging: false,// 关闭日志打印
).then((canvas)=> {
let imageUrI = canvas.toDataURL('image/jpg"); // 将canvas转成base64图片格式
state imageList.push({
imageUrl,
path,
index,
})
// 最后一个页面截图结束
if (index == 23) {
// 使用store将图片保存
store.dispatch('setImageList',state.imageList)
}
})
}
// 销毁iframe
const destroyIframe = (index) => {
const thisNode = document.querySelector(`.class-${index}`);
if (thisNode) {
thisNode.src = "about:blank";
document.getElementById(`swiper-class-${index}`).removeChild(thisNode);
}
}
// 轮播切换事件
const onslideChange = (e) => {
let index = e.detail[0]?.activeIndex;
let path = arr[index].path;o
let name = arr[index].name;
// 切换下一页,销毁上一个iframe
if(index > 0){
destroyIframe(index - 1)
}
// 如果定时器存在,清空
if (state.timer1) {
clearTimeout(state.timer1);
state.timer1 = null;
}
// 设置加载10s后进行截图
state.timer1 = setTimeout(() =>
htmlToCanvas(path,index,name)
},10000)
// 最后一个页面
if (e.detail[0]?.activeIndex === 23){
if (state.timer1){
clearTimeout(state.timer1);
state.timer1 = null;
}
state.timer1 = setTimeout(() => {
htmlToCanvas("/lastpath",23,name)
},12500)
if (state.timer2){
clearTimeout(state.timer2);
state.timer2 = null;
}
// 跳转到图片轮播页
state.timer2 = setTimeout(() => (
router.push({ path:'/carousel' }),
},14000)
}
}
onMounted(() =>{
store.dispatch('setIsIframe',true);
// 初始化时,处理第一页内容
state.timer1 = setTimeout(() => {
htmlToCanvas('/firstPage',0,'firstPage')
}, 10000)
})
图片轮播页面和页面轮播页面大致相同,只是将存在store里面的 imageList作为数据源,进行轮播,需要添加一下,点击跳入真实页面的click事件,就可以啦。
另外,需要注意跳转到页面上,还要监听一下用户操作,长时间没有操作,要自动跳回轮播页。
const onClick = (item) => {
// 记录当前在第几页,页面在固定时间没有操作之后会根据这个值跳转回来
store.dispatch('setSwiperItem',item);
router.push({ path: item.path });
}
html2canvas 使用后发现地图会绘制不到,查了一下在地图初始化时添加以下代码,就可以绘制到了。
const init = () => {
// 解决地图导出图片出现空白
nextTick(() => {
HTMLCanvasElement.prototypegetContext = (function (origFn){
return function (type, attributes) {
if (type === 'webgl') {
attributes = Object.assign({},attributes, {
preserveDrawingBuffer: true ,
})
}
return origFn.call(this, type, attributes)
}
})(HTMLCanvasElement.prototype.getContext)
})
}
3.实现数据每天更新:加每天凌晨定时更新数据的定时器
页面轮播部署之后,出现了一个新的问题,轮播页面会一直不动的放在浏览器上面展示,但是每天的数据是实时更新的,我们的截图没办法实时更新怎么办呢?就在图片轮播页面,加了每天凌晨定时更新数据的定时器。
代码如下:
const state = reactive({
time: '0:00:00',// 每天0点执行
interval: 1,//隔1天执行一次
runNow: false,// 是否立即执行
intervalTimer: null,
timeOutTimer:null,
})
// 返回轮播页面任务
const backSwiper = () => {
let taskTime = new Date();
// 清空当前轮播Index
store.dispatch('setSwiperItem',{});
router.push({path: '/swiper'})
}
//定时方法
const setscheduledTask = () => {
if (state.runNow) {
// 如果配置了立刻运行则立刻运行任务函数,跳转回swiper页面重新进行截屏
backSwiper();
}
let nowTime = new Date().getTime();
let timePoint = state.time.split(':').map((i) => parseInt(i))
let recent = new Date().setHours(...timePoint) // 获取执行时间的时间戳
if (recent <= nowTime) {
recent = recent + 24 * 60 * 60 *1000
}
// 未来程序执行的时间减去现在的时间,就是程序要多少秒之后执行
let doRunTime = recent - nowTime;
state.timeOutTimer = setTimeout(()=>{
setTimer();
}, doRunTime);
}
const setTimer = () =>{
console.log('进入定时器');
// 配置后的第一天x点执行
backSwiper();
// 每隔多少天再执行一次
let intTime = state.interval * 24 * 60 * 60 * 1000;
state.intervalTimer = setInterval(()=>{
backSwiper();
}, intTime);
}
onMounted(()=>{
// ...
setscheduledTask()
})
onUnmounted(()=>{
// 清除定时器
cleapInterval(state.intervalTimer);
clearTimeout(state.timeOutTimer);
})
总结
最终实现了系统的页面轮播,虽然做了优化,但是加载过渡依旧会出现浏览器内存奔溃的问题,很难避免【叹气.jpg】。目前的效果只能说是勉强可以达到要求,还是很看电脑性能和当时的网络环境之类的因素。
如果在系统最初就知道有轮播的需求,也许可以在做系统架构的时候,就考虑到轮播的特点进行设计,可能效果会比现在更好,但我们系统已经基本完成,也没有时间去调整架构(而且系统比较庞大,很难调整),只能将就实现就行。
不知道有没有大佬有更好的方法,求求建议。