This commit is contained in:
owenchen committed 2026-10-16 15:49:54 +08:00
1 parent 3b2ee8523b
commit b5d2a2ede0
17 files changed
+2061 -1484

No files matched your search

@@ -275,6 +275,81 @@ function toggleDirection(): void {
vertical.value = !vertical.value;
}
// ===== 控制条可拖拽:按住顶部握把行挪位(拖拽热区只在握把,不碰动作钮),
// ctrlOffset 为整条相对默认右上锚点的位移 =====
const ctrlOffset = ref({ x: 0, y: 0 });
let dragStart: { tx: number; ty: number; ox: number; oy: number } | null = null;
function onCtrlTouchStart(e: TouchEvent): void {
const t0 = e.touches?.[0];
if (!t0) {
return;
}
dragStart = { tx: t0.clientX, ty: t0.clientY, ox: ctrlOffset.value.x, oy: ctrlOffset.value.y };
}
function onCtrlTouchMove(e: TouchEvent): void {
const t0 = e.touches?.[0];
if (!dragStart || !t0) {
return;
}
const sys = uni.getSystemInfoSync();
const barW = uni.upx2px(88);
const barH = uni.upx2px(384); // 握把 + 4 动作钮各 76 + 分隔线
const edge = uni.upx2px(16);
// 默认锚点(top/right 定位)换算成 left/top 基准,再按屏幕边界夹取
const baseLeft = sys.windowWidth - uni.upx2px(24) - barW;
const baseTop = uni.upx2px(120) + (sys.safeAreaInsets?.top || 0);
const minX = edge - baseLeft;
const maxX = sys.windowWidth - edge - barW - baseLeft;
const minY = edge + uni.upx2px(110) + (sys.safeAreaInsets?.top || 0) - baseTop;
const maxY = sys.windowHeight - edge - barH - baseTop;
ctrlOffset.value = {
x: Math.min(Math.max(dragStart.ox + t0.clientX - dragStart.tx, minX), maxX),
y: Math.min(Math.max(dragStart.oy + t0.clientY - dragStart.ty, minY), maxY),
};
}
function onCtrlTouchEnd(): void {
dragStart = null;
}
// ===== 双指捏合缩放:画布触摸层两指距离比换算 zoom(5% 步进,比 ± 钮细腻)=====
let pinchStartDist = 0;
let pinchStartZoom = 1;
function touchGap(t0: Touch, t1: Touch): number {
return Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);
}
function onChartTouchStart(e: TouchEvent): void {
if (e.touches?.length === 2) {
pinchStartDist = touchGap(e.touches[0], e.touches[1]);
pinchStartZoom = zoom.value;
} else {
pinchStartDist = 0;
}
}
function onChartTouchMove(e: TouchEvent): void {
if (e.touches?.length !== 2 || pinchStartDist <= 0) {
return;
}
const ratio = touchGap(e.touches[0], e.touches[1]) / pinchStartDist;
const next = Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, Math.round(pinchStartZoom * ratio * 20) / 20));
if (next !== zoom.value) {
// H5:捏合期间拦掉默认滚动,避免双指被 scroll-view 当成拖动
e.preventDefault?.();
zoom.value = next;
}
}
function onChartTouchEnd(e: TouchEvent): void {
if (!e.touches || e.touches.length < 2) {
pinchStartDist = 0;
}
}
function requestLoad(personId: string): void {
emit("select-child", personId);
}
@@ -457,15 +532,18 @@ function close(): void {
<text class="ftc__title">
{{ props.title || t('FamilyTreePage.pedigreeChart') }}
</text>
<!-- 退出钮统一为「取消」文字胶囊(底部抽屉风格语言,不再用 X) -->
<view class="ftc__cancel" @tap="close">
<text class="ftc__cancel-text">
{{ t("FamilyTreePage.cancel") }}
</text>
<!-- 退出钮按用户要求改为 × 关闭图标(灰底圆钮) -->
<view class="ftc__close" @tap="close">
<u-icon name="close" size="28" color="#5c626b" />
</view>
</view>
<!-- 悬浮控制条:放大/缩小/百分比复位/横纵布局切换(对齐旧图 G6 工具钮) -->
<view class="ftc__ctrl">
<!-- 悬浮控制条:放大/缩小/百分比复位/横纵布局切换(对齐旧图 G6 工具钮);
会挡树图内容,按住顶部握把行可拖拽挪位;动作钮只响应单击,与拖拽互不干扰 -->
<view class="ftc__ctrl" :style="{ transform: `translate(${ctrlOffset.x}px, ${ctrlOffset.y}px)` }">
<view class="ftc__ctrl-grip" @touchstart="onCtrlTouchStart" @touchmove="onCtrlTouchMove"
@touchend="onCtrlTouchEnd" @touchcancel="onCtrlTouchEnd">
<text class="ftc__ctrl-grip-dot">⠿</text>
</view>
<view class="ftc__ctrl-btn" @tap="zoomIn">
<text class="ftc__ctrl-glyph">+</text>
</view>
@@ -479,7 +557,9 @@ function close(): void {
<text class="ftc__ctrl-glyph">⇅</text>
</view>
</view>
<scroll-view class="ftc__scroll" scroll-x scroll-y :show-scrollbar="true">
<!-- 画布:双指捏合缩放(touch 层只识别两指,单指滚动不受影响) -->
<scroll-view class="ftc__scroll" scroll-x scroll-y :show-scrollbar="true" @touchstart="onChartTouchStart"
@touchmove="onChartTouchMove" @touchend="onChartTouchEnd" @touchcancel="onChartTouchEnd">
<view v-if="hasRoot" class="ftc__tree" :style="{ zoom }">
<family-tree-node :person-id="rootPersonId" :preview="null" :expandable="true" :depth="0" />
</view>
@@ -518,18 +598,17 @@ function close(): void {
color: #1f2329;
}
.ftc__cancel {
padding: 12rpx 40rpx;
border-radius: 999rpx;
.ftc__close {
display: flex;
align-items: center;
justify-content: center;
width: 64rpx;
height: 64rpx;
border-radius: 50%;
background-color: #f2f3f5;
}
.ftc__cancel-text {
font-size: 26rpx;
color: #5c626b;
}
/* 悬浮控制条:右侧竖排,白底圆角轻阴影 */
/* 悬浮控制条:右侧竖排,白底圆角轻阴影;整条可拖拽(见 ctrlOffset) */
.ftc__ctrl {
position: absolute;
top: calc(120rpx + env(safe-area-inset-top));
@@ -543,6 +622,24 @@ function close(): void {
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.12);
}
/* 顶部握把行:拖拽热区(touch-action:none 只加在这里,动作钮手势不受影响);
与动作钮等高,手指易按住 */
.ftc__ctrl-grip {
display: flex;
align-items: center;
justify-content: center;
height: 76rpx;
border-bottom: 2rpx solid #f2f3f5;
cursor: move;
touch-action: none;
}
.ftc__ctrl-grip-dot {
font-size: 22rpx;
line-height: 1;
color: #c0c4cc;
}
.ftc__ctrl-btn {
display: flex;
align-items: center;