diff --git a/example/app.json b/example/app.json
index c5de3e8a8..ebfc409aa 100644
--- a/example/app.json
+++ b/example/app.json
@@ -56,7 +56,8 @@
"pages/navbar/navbar",
"pages/date-time-picker/date-time-picker",
"pages/action-sheet/action-sheet",
- "pages/notice-bar/notice-bar"
+ "pages/notice-bar/notice-bar",
+ "pages/image-viewer/image-viewer"
],
"usingComponents": {
"t-demo": "../../components/demo-block/index",
@@ -123,7 +124,8 @@
"t-navbar": "tdesign-miniprogram/navbar/navbar",
"t-date-time-picker": "tdesign-miniprogram/date-time-picker/date-time-picker",
"t-action-sheet": "tdesign-miniprogram/action-sheet/action-sheet",
- "t-notice-bar": "tdesign-miniprogram/notice-bar/notice-bar"
+ "t-notice-bar": "tdesign-miniprogram/notice-bar/notice-bar",
+ "t-image-viewer": "tdesign-miniprogram/image-viewer/image-viewer"
},
"window": {
"backgroundTextStyle": "light",
diff --git a/example/pages/home/data/display.ts b/example/pages/home/data/display.ts
index 89a6d6ed7..339fd0bf1 100644
--- a/example/pages/home/data/display.ts
+++ b/example/pages/home/data/display.ts
@@ -54,10 +54,10 @@ const display = {
name: 'Image',
label: '图片',
},
- // {
- // name: 'Preview',
- // label: '图片预览',
- // },
+ {
+ name: 'ImageViewer',
+ label: '图片预览',
+ },
{
name: 'Swiper',
label: '轮播图',
diff --git a/example/pages/image-viewer/image-viewer.json b/example/pages/image-viewer/image-viewer.json
new file mode 100644
index 000000000..708cd5713
--- /dev/null
+++ b/example/pages/image-viewer/image-viewer.json
@@ -0,0 +1,7 @@
+{
+ "navigationBarTitleText": "ImageViewer",
+ "navigationBarBackgroundColor": "#fff",
+ "usingComponents": {
+ "t-demo": "../../components/demo-block/index"
+ }
+}
diff --git a/example/pages/image-viewer/image-viewer.less b/example/pages/image-viewer/image-viewer.less
new file mode 100644
index 000000000..5e22e43af
--- /dev/null
+++ b/example/pages/image-viewer/image-viewer.less
@@ -0,0 +1,62 @@
+.image-viewer {
+ background-color: #fff;
+ font-size: 32rpx;
+ line-height: 48rpx;
+ color: rgba(0, 0, 0, 0.9);
+ padding: 48rpx 0rpx 96rpx 0rpx;
+ height: 100vh;
+
+ .slot-wrap {
+ background-color: #fff;
+ padding: 10px;
+ }
+
+ .title {
+ font-weight: bold;
+ font-size: 40rpx;
+ line-height: 56rpx;
+ color: rgba(0, 0, 0, 0.9);
+ padding: 0 32rpx;
+ }
+
+ .desc {
+ margin-top: 16rpx;
+ font-size: 26rpx;
+ line-height: 36rpx;
+ color: rgba(0, 0, 0, 0.4);
+ padding: 0 32rpx;
+ }
+
+ .sub-title {
+ margin-top: 40rpx;
+ font-weight: bold;
+ padding: 0 32rpx;
+ }
+
+ .t-button {
+ width: 686rpx;
+ height: 96rpx;
+ border-radius: 8rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-top: 32rpx;
+
+ &::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 200%;
+ height: 200%;
+ transform: scale(0.5);
+ transform-origin: 0 0;
+ box-sizing: border-box;
+ border: 2rpx solid #dcdcdc;
+ }
+ }
+
+ .demo-block__oper {
+ padding: 0 32rpx;
+ }
+}
diff --git a/example/pages/image-viewer/image-viewer.ts b/example/pages/image-viewer/image-viewer.ts
new file mode 100644
index 000000000..c7b1801c5
--- /dev/null
+++ b/example/pages/image-viewer/image-viewer.ts
@@ -0,0 +1,136 @@
+import Toast from 'tdesign-miniprogram/toast/index';
+
+Page({
+ data: {
+ visible: false,
+ showIndex: false,
+ closeBtn: false,
+ deleteBtn: false,
+ images: [],
+ operList1: [
+ {
+ title: '图片预览类型',
+ btns: [
+ {
+ type: 'basic',
+ text: '基础图片预览',
+ },
+ {
+ type: 'withDeleteBasic',
+ text: '有删除操作',
+ },
+ {
+ type: 'withOverHeightBasic',
+ text: '图片超高情况',
+ },
+ {
+ type: 'withOverWidthBasic',
+ text: '图片超宽情况',
+ },
+ ],
+ },
+ ],
+ },
+
+ onReady() {
+ //
+ },
+
+ clickHandle(e: any) {
+ const { detail } = e;
+ switch (detail) {
+ case 'basic':
+ this.setData({
+ images: [
+ ...Array.from({ length: 6 }).fill(
+ 'https://oteam-tdesign-1258344706.cos.ap-guangzhou.myqcloud.com/miniprogram/images/preview1.png',
+ ),
+ ],
+ showIndex: true,
+ visible: true,
+ });
+ break;
+ case 'withDeleteBasic':
+ this.setData({
+ images: [
+ ...Array.from({ length: 6 }).fill(
+ 'https://oteam-tdesign-1258344706.cos.ap-guangzhou.myqcloud.com/miniprogram/images/preview4.png',
+ ),
+ ],
+ showIndex: true,
+ visible: true,
+ closeBtn: true,
+ deleteBtn: true,
+ });
+ break;
+ case 'withOverHeightBasic':
+ this.setData({
+ images: [
+ ...Array.from({ length: 6 }).fill(
+ 'https://oteam-tdesign-1258344706.cos.ap-guangzhou.myqcloud.com/miniprogram/images/preview3.png',
+ ),
+ ],
+ showIndex: true,
+ visible: true,
+ });
+ break;
+ case 'withOverWidthBasic':
+ this.setData({
+ images: [
+ ...Array.from({ length: 6 }).fill(
+ 'https://oteam-tdesign-1258344706.cos.ap-guangzhou.myqcloud.com/miniprogram/images/preview2.png',
+ ),
+ ],
+ showIndex: true,
+ visible: true,
+ });
+ break;
+ default:
+ break;
+ }
+ },
+
+ onChange(e: any) {
+ const {
+ detail: { index },
+ } = e;
+ Toast({
+ context: this,
+ selector: '#t-toast',
+ message: `翻到第${index + 1}个`,
+ });
+ },
+
+ onDelete(e: any) {
+ const {
+ detail: { index },
+ } = e;
+ Toast({
+ context: this,
+ selector: '#t-toast',
+ message: `删除第${index + 1}个`,
+ });
+ },
+
+ onClose(e: any) {
+ const {
+ detail: { trigger },
+ } = e;
+ if (trigger === 'overlay') {
+ Toast({
+ context: this,
+ selector: '#t-toast',
+ message: '点击overlay关闭',
+ });
+ } else if (trigger === 'button') {
+ Toast({
+ context: this,
+ selector: '#t-toast',
+ message: `点击button关闭`,
+ });
+ }
+ this.setData({
+ visible: false,
+ });
+ },
+});
diff --git a/example/pages/image-viewer/image-viewer.wxml b/example/pages/image-viewer/image-viewer.wxml
new file mode 100644
index 000000000..a92e42df3
--- /dev/null
+++ b/example/pages/image-viewer/image-viewer.wxml
@@ -0,0 +1,16 @@
+
+ ImageViewer 图片预览
+ 图片全屏放大预览效果,包含全屏背景色、页码位置样式、增加操作等规范
+
+
+
+
diff --git a/example/project.config.json b/example/project.config.json
index ac76fc5cb..42a3e1528 100644
--- a/example/project.config.json
+++ b/example/project.config.json
@@ -86,6 +86,12 @@
"name": "actionSheet",
"pathName": "pages/action-sheet/action-sheet",
"scene": null
+ },
+ {
+ "id": -1,
+ "name": "image-viewer",
+ "pathName": "pages/image-viewer/image-viewer",
+ "scene": null
}
]
}
diff --git a/src/image-viewer/README.md b/src/image-viewer/README.md
new file mode 100644
index 000000000..44c28b5b5
--- /dev/null
+++ b/src/image-viewer/README.md
@@ -0,0 +1,100 @@
+---
+title: ImageViewer 图片预览
+description: 图片全屏放大预览效果,包含全屏背景色、页码位置样式、增加操作等规范。
+spline: data
+isComponent: true
+---
+
+## 引入
+
+全局引入,在 miniprogram 根目录下的`app.json`中配置,局部引入,在需要引入的页面或组件的`index.json`中配置。
+
+```json
+"usingComponents": {
+ "t-image-viewer": "tdesign-miniprogram/image-viewer/image-viewer",
+}
+```
+
+## 代码演示
+
+### 基础用法
+
+```html
+
+```
+
+### 显示页码
+
+```html
+
+```
+
+### 带删除操作
+
+```html
+
+```
+
+### 支持自定义操作按钮
+
+```html
+
+ 我是自定义的关闭内容
+ 我是自定义的删除内容
+
+```
+
+
+## API
+
+### ImageViewer Props
+
+名称 | 类型 | 默认值 | 说明 | 必传
+-- | -- | -- | -- | --
+background-color | String / Number | rgba(0, 0, 0, 1) | 遮罩的背景颜色 | N
+images | Array | [] | 图片数组。TS 类型:`Array` | N
+initial-index | Number | 0 | 默认展示第几项 | N
+show-index | Boolean | false | 是否显示页码 | N
+delete-btn | Boolean | false | 是否显示删除操作,前提需要开启页码 | N
+close-btn | Boolean | false | 是否显示关闭操作,前提需要开启页码 | N
+visible | Boolean | false | 隐藏/显示预览 | N
+default-visible | Boolean | undefined | 隐藏/显示预览。非受控属性 | N
+
+
+
+### ImageViewer Events
+
+名称 | 参数 | 描述
+-- | -- | --
+change | `(index: Number)` | 翻页时回调
+close | `(trigger: 'overlay' | 'button' , visible: Boolean, index: Number)` | 点击操作按钮button或者overlay时触发
+delete | `(index: Number)` | 点击删除操作按钮时触发
+
diff --git a/src/image-viewer/image-viewer.json b/src/image-viewer/image-viewer.json
new file mode 100644
index 000000000..57fe7ea66
--- /dev/null
+++ b/src/image-viewer/image-viewer.json
@@ -0,0 +1,9 @@
+{
+ "component": true,
+ "usingComponents": {
+ "t-image": "../image/image",
+ "t-icon": "../icon/icon",
+ "t-swiper": "../swiper/swiper",
+ "t-swiper-item": "../swiper/swiper-item"
+ }
+}
diff --git a/src/image-viewer/image-viewer.less b/src/image-viewer/image-viewer.less
new file mode 100644
index 000000000..57d73c355
--- /dev/null
+++ b/src/image-viewer/image-viewer.less
@@ -0,0 +1,81 @@
+@import '../common/style/index.less';
+
+@image-viewer: ~'@{prefix}-image-viewer';
+@image-viewer-nav-height: 48rpx;
+@image-viewer-nav-margin: 36rpx;
+
+.@{image-viewer} {
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ z-index: 1001;
+ height: 100%;
+ transform: translateZ(0);
+ overflow: hidden;
+
+ &__mask {
+ position: fixed;
+ z-index: 1000;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ }
+
+ &__content {
+ width: 100vw;
+ display: inline-block;
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ z-index: 1005;
+ }
+
+ &__image {
+ width: 100%;
+ display: inline-block;
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ image {
+ width: inherit !important;
+ height: inherit !important;
+ display: block;
+ }
+ }
+
+ &__nav {
+ width: 100%;
+ position: fixed;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: @image-viewer-nav-height;
+ top: 68rpx;
+ left: 0;
+ color: #ffffff;
+ z-index: 1005;
+
+ &-icon {
+ flex: 0 0 @image-viewer-nav-height;
+ width: @image-viewer-nav-height;
+ height: @image-viewer-nav-height;
+ }
+
+ &-close {
+ margin-left: @image-viewer-nav-margin;
+ }
+
+ &-delete {
+ margin-right: @image-viewer-nav-margin;
+ }
+
+ &-index {
+ flex: 1;
+ font-size: 28rpx;
+ text-align: center;
+ }
+ }
+}
diff --git a/src/image-viewer/image-viewer.ts b/src/image-viewer/image-viewer.ts
new file mode 100644
index 000000000..79ce39b5e
--- /dev/null
+++ b/src/image-viewer/image-viewer.ts
@@ -0,0 +1,150 @@
+import { styles } from '../common/utils';
+import { SuperComponent, wxComponent } from '../common/src/index';
+import config from '../common/config';
+import props from './props';
+
+const { prefix } = config;
+const name = `${prefix}-image-viewer`;
+
+@wxComponent()
+export default class ImageViewer extends SuperComponent {
+ externalClasses = [`${prefix}-class`];
+
+ properties = {
+ ...props,
+ };
+
+ data = {
+ prefix,
+ classPrefix: name,
+ currentSwiperIndex: 0,
+ windowHeight: 0,
+ windowWidth: 0,
+ imagesShape: {},
+ };
+
+ options = {
+ multipleSlots: true,
+ };
+
+ controlledProps = [
+ {
+ key: 'visible',
+ event: 'close',
+ },
+ ];
+
+ ready() {
+ this.saveScreenSize();
+ }
+
+ observers = {
+ visible(value) {
+ this.setData({
+ currentSwiperIndex: value ? this.properties.initialIndex : 0,
+ });
+ },
+ };
+
+ methods = {
+ saveScreenSize() {
+ const { windowHeight, windowWidth } = wx.getSystemInfoSync();
+ this.setData({
+ windowHeight,
+ windowWidth,
+ });
+ },
+
+ calcImageDisplayStyle(imageWidth, imageHeight) {
+ const { windowWidth, windowHeight } = this.data;
+ // 图片宽高都小于屏幕宽高
+ if (imageWidth < windowWidth && imageHeight < windowHeight) {
+ return {
+ mode: 'scaleToFill',
+ styleObj: {
+ width: '100%',
+ height: `${imageHeight * 2}rpx`,
+ },
+ };
+ }
+
+ // 图片宽高都大等于屏幕宽高
+ if (imageWidth >= windowWidth && imageHeight >= windowHeight) {
+ return {
+ mode: 'aspectFit',
+ styleObj: {
+ width: '100%',
+ height: `${(imageHeight / (imageWidth / windowWidth)) * 2}rpx`,
+ },
+ };
+ }
+
+ // 图片超高:图片宽小于屏幕宽,图片高大于等于屏幕高
+ if (imageWidth < windowWidth && imageHeight >= windowHeight) {
+ return {
+ mode: 'widthFix',
+ styleObj: {
+ width: `${(imageWidth / (imageHeight / windowHeight)) * 2}rpx`,
+ height: '100vh',
+ left: '50%',
+ transform: 'translate(-50%, -50%)',
+ },
+ };
+ }
+
+ // 图片超宽:图片宽大于等于屏幕宽,图片高小于屏幕高
+ if (imageWidth >= windowWidth && imageHeight < windowHeight) {
+ return {
+ mode: 'heightFix',
+ styleObj: {
+ width: '100%',
+ height: `${(imageHeight / (imageWidth / windowWidth)) * 2}rpx`,
+ },
+ };
+ }
+ },
+
+ onImageLoadSuccess(e: WechatMiniprogram.TouchEvent) {
+ const {
+ detail: { width, height },
+ currentTarget: {
+ dataset: { index },
+ },
+ } = e;
+ const { mode, styleObj } = this.calcImageDisplayStyle(width, height);
+ const origin = this.data.imagesShape;
+ this.setData({
+ imagesShape: {
+ ...origin,
+ [index]: {
+ mode,
+ style: styles({ ...styleObj }),
+ },
+ },
+ });
+ },
+
+ onSwiperChange(e: WechatMiniprogram.TouchEvent) {
+ const {
+ detail: { current },
+ } = e;
+ this.setData({
+ currentSwiperIndex: current,
+ });
+ this._trigger('change', { index: current });
+ },
+
+ onClose(e: WechatMiniprogram.TouchEvent) {
+ const {
+ target: {
+ dataset: { source },
+ },
+ } = e;
+ this._trigger('close', { visible: false, trigger: source, index: this.data.currentSwiperIndex });
+ },
+
+ onDelete() {
+ this._trigger('delete', { index: this.data.currentSwiperIndex });
+ },
+ };
+}
diff --git a/src/image-viewer/image-viewer.wxml b/src/image-viewer/image-viewer.wxml
new file mode 100644
index 000000000..2ce474e4c
--- /dev/null
+++ b/src/image-viewer/image-viewer.wxml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{currentSwiperIndex + 1}}/{{images.length}}
+
+
+
+
+
+
diff --git a/src/image-viewer/props.ts b/src/image-viewer/props.ts
new file mode 100644
index 000000000..4dc23fa01
--- /dev/null
+++ b/src/image-viewer/props.ts
@@ -0,0 +1,52 @@
+/* eslint-disable */
+
+/**
+ * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
+ * */
+
+import { TdImageViewerProps } from './type';
+const props: TdImageViewerProps = {
+ /** 遮罩的背景颜色 */
+ backgroundColor: {
+ type: String,
+ optionalTypes: [Number],
+ value: 'rgba(0, 0, 0, 1)',
+ },
+ /** 图片数组 */
+ images: {
+ type: Array,
+ value: [],
+ },
+ /** 默认展示第几项 */
+ initialIndex: {
+ type: Number,
+ value: 0,
+ },
+ /** 是否显示页码 */
+ showIndex: {
+ type: Boolean,
+ value: false,
+ },
+ /** 是否显示删除操作 */
+ deleteBtn: {
+ type: Boolean,
+ value: false,
+ },
+ /** 是否显示关闭操作 */
+ closeBtn: {
+ type: Boolean,
+ value: false,
+ },
+ /** 隐藏/显示预览 */
+ visible: {
+ type: Boolean,
+ value: null,
+ },
+ /** 隐藏/显示预览,非受控属性 */
+ defaultVisible: {
+ type: Boolean,
+ value: false,
+ },
+};
+
+export default props;
diff --git a/src/image-viewer/type.ts b/src/image-viewer/type.ts
new file mode 100644
index 000000000..ba6af79ff
--- /dev/null
+++ b/src/image-viewer/type.ts
@@ -0,0 +1,67 @@
+/* eslint-disable */
+
+/**
+ * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
+ * */
+
+export interface TdImageViewerProps {
+ /**
+ * 遮罩的背景颜色
+ * @default rgba(0, 0, 0, .6)
+ */
+ backgroundColor?: {
+ type: StringConstructor;
+ optionalTypes: Array;
+ value?: string | number;
+ };
+ /**
+ * 图片数组
+ * @default []
+ */
+ images?: {
+ type: ArrayConstructor;
+ value?: Array;
+ };
+ /**
+ * 默认展示第几项
+ * @default 0
+ */
+ initialIndex?: {
+ type: NumberConstructor;
+ value?: number;
+ };
+ /**
+ * 是否显示页码
+ * @default false
+ */
+ showIndex?: {
+ type: BooleanConstructor;
+ value?: boolean;
+ };
+ /** 是否显示删除操作 */
+ deleteBtn?: {
+ type: BooleanConstructor;
+ value: false;
+ };
+ /** 是否显示关闭操作 */
+ closeBtn?: {
+ type: BooleanConstructor;
+ value: false;
+ };
+ /**
+ * 隐藏/显示预览
+ * @default false
+ */
+ visible?: {
+ type: BooleanConstructor;
+ value?: boolean;
+ };
+ /**
+ * 隐藏/显示预览,非受控属性
+ * @default false
+ */
+ defaultVisible?: {
+ type: BooleanConstructor;
+ value?: boolean;
+ };
+}