多页面系统,实现页面轮播展示 + 每天定时更新数据

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

系统大概构成: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】。目前的效果只能说是勉强可以达到要求,还是很看电脑性能和当时的网络环境之类的因素。
如果在系统最初就知道有轮播的需求,也许可以在做系统架构的时候,就考虑到轮播的特点进行设计,可能效果会比现在更好,但我们系统已经基本完成,也没有时间去调整架构(而且系统比较庞大,很难调整),只能将就实现就行。
不知道有没有大佬有更好的方法,求求建议。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用 `UICollectionView` 或 `UIPageViewController` 来实现 iOS 图片轮播。 使用 `UICollectionView` 实现的方法如下: 1. 在您的视图控制器中,创建一个 `UICollectionView` 实例,并将其作为子视图添加到您的视图控制器的视图中。 2. 使用自定义布局实现循环滚动。可以参考以下代码: ``` class LoopCollectionViewFlowLayout: UICollectionViewFlowLayout { override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { guard let collectionView = collectionView else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) } let collectionViewSize = collectionView.bounds.size let proposedContentOffsetCenterX = proposedContentOffset.x + collectionViewSize.width * 0.5 let proposedRect = CGRect(x: proposedContentOffset.x, y: 0, width: collectionViewSize.width, height: collectionViewSize.height) guard let layoutAttributes = layoutAttributesForElements(in: proposedRect) else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) } let centerX = proposedContentOffsetCenterX let offset = CGPoint(x: proposedContentOffset.x + nearestTargetOffset(for: layoutAttributes, with: centerX), y: proposedContentOffset.y) return offset } private func nearestTargetOffset(for layoutAttributes: [UICollectionViewLayoutAttributes], with centerX: CGFloat) -> CGFloat { let targetAttributes = layoutAttributes.sorted { abs($0.center.x - centerX) < abs($1.center.x - centerX) } let nearestAttribute = targetAttributes.first return nearestAttribute?.center.x ?? 0 - centerX } } ``` 3. 创建自定义 `UICollectionViewCell` 类,并在其中添加一个 `UIImageView` 用于显示图片。 4. 实现 `UICollectionViewDataSource` 协议中的方法,用于设置图片数据源和自定义的 `UICollectionViewCell`。 5. 实现定时

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值