-
Notifications
You must be signed in to change notification settings - Fork 361
feat(ImageViewer): add transform utils and zoom types #2461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+179
−8
Merged
Changes from 4 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
c362380
feat(ImageViewer): add transform utils and zoom types
RSS1102 cd42731
refactor(image-viewer): 导出类型
RSS1102 c698a0e
refactor(image-viewer): 提取通用类型和工具函数
RSS1102 946a1bf
Merge branch 'develop' into rss1102/feat/image-viewer-drag
RSS1102 74a1842
refactor(image-viewer): 移动 DEFAULT_IMAGE_SCALE 到 transform.ts
RSS1102 086d005
refactor: 移除 math.ts 文件
RSS1102 00c1c50
fix: 函数引用路径错误
RSS1102 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| import { positiveAdd, positiveSubtract } from '../utils/math'; | ||
| import type { ZoomOptions, ZoomResult, TranslateOffset, ImageScale } from './types'; | ||
| import { DEFAULT_IMAGE_SCALE } from './types'; | ||
|
|
||
| export type { ZoomOptions, ZoomResult, TranslateOffset, ImageScale }; | ||
| export { DEFAULT_IMAGE_SCALE }; | ||
|
|
||
| /** | ||
| * 检测图片是否超出视口(容器)边界 | ||
| * @param container 外层容器元素 | ||
| * @param modalBox 图片包裹元素 | ||
| */ | ||
| export const isImageExceedsViewport = (container: HTMLElement, modalBox: HTMLElement): boolean => { | ||
| const containerRect = container.getBoundingClientRect(); | ||
| const modalRect = modalBox.getBoundingClientRect(); | ||
| return ( | ||
| modalRect.left < containerRect.left || | ||
| modalRect.right > containerRect.right || | ||
| modalRect.top < containerRect.top || | ||
| modalRect.bottom > containerRect.bottom | ||
| ); | ||
| }; | ||
|
|
||
| /** 镜像默认值(未镜像) */ | ||
| export const MIRROR_DEFAULT = 1; | ||
|
|
||
| /** 切换镜像状态:1 → -1,-1 → 1 */ | ||
| export const toggleMirror = (current: number): number => (current > 0 ? -1 : 1); | ||
|
|
||
| /** 每次旋转的角度(逆时针 90°) */ | ||
| export const ROTATE_DEG = -90; | ||
|
|
||
| /** | ||
| * 计算最短路径归零的旋转补偿值 | ||
| * 用于 resetRotate 场景:避免 CSS transition 时图片"倒转一大圈" | ||
| * | ||
| * @param currentDeg 当前累计旋转角度 | ||
| * @returns 需要减去的补偿值(rotate.value -= 返回值 即可归零) | ||
| * | ||
| * @example | ||
| * // currentDeg = -270 → return 90(-270 - 90 = -360 ≡ 0°) | ||
| * // currentDeg = -180 → return -180(-180 - (-180) = 0) | ||
| * // currentDeg = 0 → return 0(无需旋转) | ||
| */ | ||
| export function calcResetRotation(currentDeg: number): number { | ||
| const degreeToRotate = currentDeg % 360; | ||
| if (degreeToRotate === 0) return 0; | ||
| // 找最短方向旋转回 0° | ||
| return Math.abs(degreeToRotate) > 180 ? (degreeToRotate + 360) % 360 : degreeToRotate; | ||
| } | ||
|
|
||
| /** 将缩放值限制在 [min, max] 范围内 */ | ||
| export function clampScale(value: number, min: number, max: number): number { | ||
| return Math.max(min, Math.min(max, value)); | ||
| } | ||
|
|
||
| /** | ||
| * 计算放大后的新 scale 值(使用精确浮点数加法) | ||
| * @returns clamp 后的新 scale 值 | ||
| */ | ||
| export function calcZoomInScale(oldScale: number, step: number, min: number, max: number): number { | ||
| return clampScale(positiveAdd(oldScale, step), min, max); | ||
| } | ||
|
|
||
| /** | ||
| * 计算缩小后的新 scale 值(使用精确浮点数减法) | ||
| * @returns clamp 后的新 scale 值 | ||
| */ | ||
| export function calcZoomOutScale(oldScale: number, step: number, min: number, max: number): number { | ||
| return clampScale(positiveSubtract(oldScale, step), min, max); | ||
| } | ||
|
|
||
| /** | ||
| * 计算缩放后的位移补偿 | ||
| * 公式:newTranslate = scaleRatio * T + (1 - scaleRatio) * Z | ||
| * 其中 Z 为缩放中心,T 为当前位移,scaleRatio = newScale / oldScale | ||
| */ | ||
| export function calculateTranslateOffset( | ||
| oldScale: number, | ||
| newScale: number, | ||
| options?: ZoomOptions | ||
| ): TranslateOffset | undefined { | ||
| if (options?.mouseOffsetX == null || options?.mouseOffsetY == null) { | ||
| return undefined; | ||
| } | ||
|
|
||
| const scaleRatio = newScale / oldScale; | ||
| const { translateX = 0, translateY = 0 } = options?.currentTranslate ?? {}; | ||
| const { mouseOffsetX, mouseOffsetY } = options; | ||
|
|
||
| return { | ||
| translateX: scaleRatio * translateX + (1 - scaleRatio) * mouseOffsetX, | ||
| translateY: scaleRatio * translateY + (1 - scaleRatio) * mouseOffsetY, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * 执行一次 zoom in 并计算位移补偿 | ||
| * @returns { newScale, zoomResult } | ||
| */ | ||
| export function zoomIn( | ||
| oldScale: number, | ||
| step: number, | ||
| min: number, | ||
| max: number, | ||
| options?: ZoomOptions | ||
| ): { newScale: number; zoomResult: ZoomResult } { | ||
| const newScale = calcZoomInScale(oldScale, step, min, max); | ||
| return { | ||
| newScale, | ||
| zoomResult: { newTranslate: calculateTranslateOffset(oldScale, newScale, options) }, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * 执行一次 zoom out 并计算位移补偿 | ||
| * @returns { newScale, zoomResult } | ||
| */ | ||
| export function zoomOut( | ||
| oldScale: number, | ||
| step: number, | ||
| min: number, | ||
| max: number, | ||
| options?: ZoomOptions | ||
| ): { newScale: number; zoomResult: ZoomResult } { | ||
| const newScale = calcZoomOutScale(oldScale, step, min, max); | ||
| return { | ||
| newScale, | ||
| zoomResult: { newTranslate: calculateTranslateOffset(oldScale, newScale, options) }, | ||
| }; | ||
| } |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| export interface ImageInfo { | ||
| mainImage: string | File; | ||
| thumbnail?: string | File; | ||
| download?: boolean; | ||
| isSvg?: boolean; | ||
| } | ||
|
|
||
| export type Images = Array<string | File | ImageInfo>; | ||
|
|
||
| /** 位移偏移量 */ | ||
| export interface TranslateOffset { | ||
| translateX: number; | ||
| translateY: number; | ||
| } | ||
|
|
||
| /** 缩放选项 */ | ||
| export interface ZoomOptions { | ||
| /** 缩放中心点 X 坐标(相对于预览图片容器中心的偏移量) */ | ||
| mouseOffsetX?: number; | ||
| /** 缩放中心点 Y 坐标(相对于预览图片容器中心的偏移量) */ | ||
| mouseOffsetY?: number; | ||
| /** 当前位移 */ | ||
| currentTranslate?: TranslateOffset; | ||
| } | ||
|
|
||
| /** 缩放结果 */ | ||
| export interface ZoomResult { | ||
| /** 缩放后的新位移 */ | ||
| newTranslate?: TranslateOffset; | ||
| } | ||
|
|
||
| /** 图片缩放配置 */ | ||
| export interface ImageScale { | ||
| /** 缩放的最大比例 */ | ||
| max: number; | ||
| /** 缩放的最小比例 */ | ||
| min: number; | ||
| /** 缩放的步长速度 */ | ||
| step: number; | ||
| /** 默认的缩放比例 */ | ||
| defaultScale: number; | ||
| } | ||
|
|
||
| /** ImageScale 的默认值,所有使用处应引用此常量,避免多处声明不一致 */ | ||
| export const DEFAULT_IMAGE_SCALE: ImageScale = { | ||
| max: 2, | ||
| min: 0.5, | ||
| step: 0.2, | ||
| defaultScale: 1, | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| /** | ||
| * 通用数学精度工具函数 | ||
| * 从 input-number/number.ts 中提取出来,供跨组件使用 | ||
| */ | ||
| export { positiveAdd, positiveSubtract } from '../input-number/number'; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.