uniapp 自定义图片预览组件PicturePreview(Vue3、组合式、ts)

组件

html 复制代码
<template>
	<view class="preview-container" :style="{display: show ? 'block' : 'none'}">
		<view class="f-c-c close" @click="close">
			<YcSvgIcon name="close" width="15rpx" height="15rpx" color="#ffffff"></YcSvgIcon>
		</view>
		<swiper class="swiper" :indicator-dots="true" :autoplay="false" :interval="5000" :duration="500"
			:circular="true" @change="onSwiperChange" :current="currentIndex" @transition="scale = 1">
			<swiper-item v-for="(image, index) in images" :key="index">
				<movable-area scale-area class="movable-area">
					<movable-view class="movable-view" :scale="true" :scale-min="1" :scale-max="1" :scale-value="scale"
						@scale="onScale" out-of-bounds>
						<image class="preview-image" :src="image" mode="aspectFit" @click="close" />
					</movable-view>
				</movable-area>
			</swiper-item>
		</swiper>
	</view>
</template>

<script setup lang="ts">
	import { ref } from 'vue';

	const images = ref<Array<string>>([]);
	const currentIndex = ref(0);
	const show = ref(false);
	const scale = ref(1);

	function onSwiperChange(e : any) {
		scale.value = 1;
		currentIndex.value = e.detail.current;
	}
	const onScale = (e : any) => {
		scale.value = e.detail.scale;
		console.log(scale.value);
	}
	const close = () => {
		images.value = [];
		currentIndex.value = 0;
		show.value = false;
	}
	const previewImage = (imgs : Array<string>, index : number) => {
		images.value = imgs;
		currentIndex.value = index;
		show.value = true;
	}
	defineExpose({
		previewImage,
	})
</script>

<style scoped lang="scss">
	.preview-container {
		position: fixed;
		top: 0;
		left: 0;
		width: 100vw;
		height: 100vh;
		overflow: hidden;
		z-index: 99;
		background-color: rgba(0, 0, 0, 0.8);

		.close {
			position: absolute;
			top: 25rpx;
			right: 25rpx;
			z-index: 999;
		}

		.swiper {
			width: 100%;
			height: 100%;

			.movable-area {
				width: 100%;
				height: 100%;

				.movable-view {
					width: 100%;
					height: 100%;
				}
			}

			.preview-image {
				width: 100%;
				height: 100%;
			}
		}
	}
</style>

组件使用

html 复制代码
<template>
	<view class="photography-page view-container">
		<YcPicturePreview ref="preview" />
	</view>
</template>

<script setup lang="ts">
	import { ref, computed } from 'vue';
	import YcPicturePreview from "@/components/YcPicturePreview/YcPicturePreview.vue";
	let preview = ref<InstanceType<typeof YcPicturePreview>>();
	const photos = ref([
		{
			id: 1,
			url: 'https://img2.baidu.com/it/u=1028011339,1319212411&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=313',
			selected: false,
		},
		{
			id: 2,
			url: 'https://img0.baidu.com/it/u=1668365367,2821596851&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=684',
			selected: true,
		},
		{
			id: 3,
			url: 'https://img0.baidu.com/it/u=1668365367,2821596851&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=684',
			selected: false,
		},
		{
			id: 4,
			url: 'https://img0.baidu.com/it/u=1668365367,2821596851&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=684',
			selected: false,
		},
		{
			id: 5,
			url: 'https://img0.baidu.com/it/u=1668365367,2821596851&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=684',
			selected: false,
		},
		{
			id: 6,
			url: 'https://img0.baidu.com/it/u=1668365367,2821596851&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=684',
			selected: false,
		},

		{
			id: 7,
			url: 'https://img0.baidu.com/it/u=1668365367,2821596851&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=684',
			selected: false,
		},
		{
			id: 8,
			url: 'https://img0.baidu.com/it/u=1668365367,2821596851&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=684',
			selected: false,
		},
		{
			id: 9,
			url: 'https://img0.baidu.com/it/u=1668365367,2821596851&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=684',
			selected: false,
		},
		{
			id: 10,
			url: 'https://img0.baidu.com/it/u=1668365367,2821596851&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=684',
			selected: false,
		},

		{
			id: 11,
			url: 'https://img0.baidu.com/it/u=1668365367,2821596851&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=684',
			selected: false,
		},
	])
	const photoUrls = computed(() => {
		return photos.value.map(item => item.url)
	})
	const showBig = (currentIndex : number, list : Array<string>) => {
		preview.value?.previewImage(list, currentIndex);
	}
</script>

<style lang="scss" scoped>
 // ...
</style>
相关推荐
deli00729 分钟前
32K 上下文塞满后,AI 最先忘掉什么?120 轮对话实测
前端
deli0071 小时前
手机拍灯为什么有一条条黑纹?PWM 240Hz 实测
前端
wangruofeng6 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng6 小时前
给前端开发者的 100 个设计参考网站
前端·设计
M--Y9 小时前
LangGraph 工作流模式
开发语言·javascript·python·langchain·大模型·llm·langgraph
星栈9 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技9 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户479492835691510 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马12 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng13 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构