提交 65a8ff94 编写于 作者: Q qiang

feat(h5): previewImage

上级 81e35824
......@@ -37,7 +37,7 @@
"node": ">=10.0.0"
},
"devDependencies": {
"@dcloudio/types": "^2.2.1",
"@dcloudio/types": "^2.2.3",
"@microsoft/api-extractor": "^7.13.2",
"@rollup/plugin-alias": "^3.1.1",
"@rollup/plugin-commonjs": "^17.0.0",
......
......@@ -37,6 +37,7 @@ export * from './protocols/media/chooseImage'
export * from './protocols/media/chooseVideo'
export * from './protocols/media/chooseFile'
export * from './protocols/media/getImageInfo'
export * from './protocols/media/previewImage'
export * from './protocols/media/getVideoInfo'
export * from './protocols/network/request'
......
import { getRealPath } from '@dcloudio/uni-platform'
export const API_PREVIEW_IMAGE = 'previewImage'
export type API_TYPE_PREVIEW_IMAGE = typeof uni.previewImage
export const PreviewImageOptions: ApiOptions<API_TYPE_PREVIEW_IMAGE> = {
formatArgs: {
urls(urls, params) {
params.urls = urls.map((url) =>
typeof url === 'string' && url ? getRealPath(url) : ''
)
},
current(current, params) {
if (typeof current === 'number') {
params.current =
current > 0 && current < params.urls.length ? current : 0
} else if (typeof current === 'string' && current) {
params.current = getRealPath(current)
}
},
},
}
export const PreviewImageProtocol: ApiProtocol<API_TYPE_PREVIEW_IMAGE> = {
urls: {
type: Array,
required: true,
},
current: {
type: [Number, String],
},
}
......@@ -7,7 +7,7 @@ export type CustomEventTrigger = ReturnType<typeof useCustomEvent>
export type NativeEventTrigger = ReturnType<typeof useNativeEvent>
export type EmitEvent<E extends (...args: any) => any> = [Parameters<E>[0]]
export function withWebEvent(fn: Function) {
export function withWebEvent(fn: (e: any) => any) {
return ((fn as any).__wwe = true), fn
}
......
此差异已折叠。
此差异已折叠。
......@@ -31,6 +31,7 @@ export * from './media/getImageInfo'
export * from './media/getVideoInfo'
export * from './media/chooseFile'
export * from './media/chooseImage'
export * from './media/previewImage'
export * from './media/chooseVideo'
export * from './network/request'
......
import {
defineComponent,
PropType,
onMounted,
Ref,
ref,
ExtractPropTypes,
watch,
nextTick,
} from 'vue'
import { Swiper, SwiperItem } from '@dcloudio/uni-components'
import ImageView from './ImageView'
const props = {
urls: {
type: Array as PropType<string[]>,
default() {
return []
},
},
current: {
type: [Number, String],
default: 0,
},
}
export type Props = ExtractPropTypes<typeof props>
function getIndex(props: Props): number {
let index =
typeof props.current === 'number'
? props.current
: props.urls.indexOf(props.current)
index = index < 0 ? 0 : index
return index
}
export default /*#__PURE__*/ defineComponent({
props,
setup(props, { emit }) {
const rootRef: Ref<HTMLElement | null> = ref(null)
const indexRef = ref(getIndex(props))
watch(
() => props.current,
() => (indexRef.value = getIndex(props))
)
let preventDefault: boolean
onMounted(() => {
const el = rootRef.value as HTMLElement
const MAX_MOVE = 20
let x = 0
let y = 0
el.addEventListener('mousedown', (event) => {
preventDefault = false
x = event.clientX
y = event.clientY
})
el.addEventListener('mouseup', (event) => {
if (
Math.abs(event.clientX - x) > MAX_MOVE ||
Math.abs(event.clientY - y) > MAX_MOVE
) {
preventDefault = true
}
})
})
function onClick() {
if (!preventDefault) {
nextTick(() => {
emit('close')
})
}
}
function onChange(event: { detail: { current: number } }) {
indexRef.value = event.detail.current
}
return () => {
return (
<div
ref={rootRef}
style={{
display: 'block',
position: 'fixed',
left: '0',
top: '0',
width: '100%',
height: '100%',
zIndex: 999,
background: 'rgba(0,0,0,0.8)',
}}
onClick={onClick}
>
<Swiper
current={indexRef.value}
onChange={onChange}
indicator-dots={false}
autoplay={false}
style={{
position: 'absolute',
left: '0',
top: '0',
width: '100%',
height: '100%',
}}
>
{props.urls.map((src) => (
<SwiperItem>
<ImageView src={src} />
</SwiperItem>
))}
</Swiper>
</div>
)
}
},
})
import { defineComponent, reactive } from 'vue'
import {
MovableArea,
MovableView,
withWebEvent,
} from '@dcloudio/uni-components'
const props = {
src: {
type: String,
default: '',
},
}
export default /*#__PURE__*/ defineComponent({
name: 'ImageView',
props,
setup(props) {
const state = reactive({
direction: 'none',
})
let scale = 1
let imgWidth = 0
let imgHeight = 0
let width = 0
let height = 0
function onScale({ detail }: { detail: { scale: number } }) {
scale = detail.scale
}
function onImgLoad(event: Event) {
const target = event.target as HTMLElement
const rect = target.getBoundingClientRect()
imgWidth = rect.width
imgHeight = rect.height
}
function onTouchStart(event: Event) {
const target = event.target as HTMLElement
const rect = target.getBoundingClientRect()
width = rect.width
height = rect.height
checkDirection(event)
}
function onTouchEnd(event: Event) {
const horizontal = scale * imgWidth > width
const vertical = scale * imgHeight > height
if (horizontal && vertical) {
state.direction = 'all'
} else if (horizontal) {
state.direction = 'horizontal'
} else if (vertical) {
state.direction = 'vertical'
} else {
state.direction = 'none'
}
checkDirection(event)
}
function checkDirection(event: Event) {
// 避免水平滑动和 swiper 冲突
if (state.direction === 'all' || state.direction === 'horizontal') {
event.stopPropagation()
}
}
return () => {
const viewStyle = {
position: 'absolute',
left: '0',
top: '0',
width: '100%',
height: '100%',
}
return (
<MovableArea
style={viewStyle}
onTouchstart={withWebEvent(onTouchStart)}
onTouchmove={withWebEvent(checkDirection)}
onTouchend={withWebEvent(onTouchEnd)}
>
<MovableView
style={viewStyle}
direction={state.direction}
inertia
scale
scale-min="1"
scale-max="4"
onScale={onScale}
>
<img
src={props.src}
style={{
position: 'absolute',
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)',
maxHeight: '100%',
maxWidth: '100%',
}}
onLoad={onImgLoad}
/>
</MovableView>
</MovableArea>
)
}
},
})
import { extend } from '@vue/shared'
import { nextTick, reactive } from 'vue'
import {
API_PREVIEW_IMAGE,
API_TYPE_PREVIEW_IMAGE,
defineAsyncApi,
PreviewImageProtocol,
PreviewImageOptions,
} from '@dcloudio/uni-api'
import { ensureRoot, createRootApp } from '../../ui/popup/utils'
import ImagePreview, { Props } from './ImagePreview'
let state: Props | null = null
export const previewImage = <API_TYPE_PREVIEW_IMAGE>defineAsyncApi(
API_PREVIEW_IMAGE,
(args, { resolve }) => {
if (!state) {
state = reactive(args) as Props
// 异步执行,避免干扰 getCurrentInstance
nextTick(() => {
const app = createRootApp(ImagePreview, state as Props, () => {
state = null
nextTick(() => {
app.unmount()
})
})
app.mount(ensureRoot('u-a-p'))
})
} else {
extend(state, args)
}
resolve()
},
PreviewImageProtocol,
PreviewImageOptions
)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册