mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-17 07:00:12 +01:00
🎨 Improve automatic scrolling when dragging items in the outline (#15846)
This commit is contained in:
parent
950f392217
commit
b3b5bbbc2b
1 changed files with 73 additions and 4 deletions
|
|
@ -36,6 +36,9 @@ export class Outline extends Model {
|
||||||
public blockId: string;
|
public blockId: string;
|
||||||
public isPreview: boolean;
|
public isPreview: boolean;
|
||||||
private preFilterExpandIds: string[] | null = null;
|
private preFilterExpandIds: string[] | null = null;
|
||||||
|
private scrollAnimationId: number | null = null;
|
||||||
|
private scrollLastFrameTime: number = 0;
|
||||||
|
private scrollCurrentFPS: number = 60;
|
||||||
|
|
||||||
constructor(options: {
|
constructor(options: {
|
||||||
app: App,
|
app: App,
|
||||||
|
|
@ -374,11 +377,68 @@ export class Outline extends Model {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 检查是否在滚动边界区域
|
||||||
if (moveEvent.clientY < contentRect.top + Constants.SIZE_SCROLL_TB || moveEvent.clientY > contentRect.bottom - Constants.SIZE_SCROLL_TB) {
|
if (moveEvent.clientY < contentRect.top + Constants.SIZE_SCROLL_TB || moveEvent.clientY > contentRect.bottom - Constants.SIZE_SCROLL_TB) {
|
||||||
|
// 如果还没有开始滚动,则开始持续滚动
|
||||||
|
if (!this.scrollAnimationId) {
|
||||||
|
const scrollDirection = moveEvent.clientY < contentRect.top + Constants.SIZE_SCROLL_TB ? -1 : 1;
|
||||||
|
this.scrollLastFrameTime = performance.now();
|
||||||
|
let scrollFrameCount = 0;
|
||||||
|
|
||||||
|
const scrollAnimation = (currentTime: number) => {
|
||||||
|
if (!this.scrollAnimationId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每隔 20 帧重新计算一次帧率
|
||||||
|
if (scrollFrameCount % 20 === 0) {
|
||||||
|
const deltaTime = currentTime - this.scrollLastFrameTime;
|
||||||
|
this.scrollLastFrameTime = currentTime;
|
||||||
|
// 计算过去 20 帧的平均帧率
|
||||||
|
this.scrollCurrentFPS = deltaTime > 0 ? (20 * 1000) / deltaTime : 60;
|
||||||
|
}
|
||||||
|
scrollFrameCount++;
|
||||||
|
|
||||||
|
// 基于当前帧率计算滚动步长,确保等效于 60fps 时的 16px/帧
|
||||||
|
const baseScrollStep = 16;
|
||||||
|
const targetFPS = 60;
|
||||||
|
const scrollStep = baseScrollStep * (targetFPS / this.scrollCurrentFPS);
|
||||||
|
|
||||||
this.element.scroll({
|
this.element.scroll({
|
||||||
top: this.element.scrollTop + (moveEvent.clientY < contentRect.top + Constants.SIZE_SCROLL_TB ? -Constants.SIZE_SCROLL_STEP : Constants.SIZE_SCROLL_STEP),
|
top: this.element.scrollTop + scrollStep * scrollDirection
|
||||||
behavior: "smooth"
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 使用 requestAnimationFrame 继续动画
|
||||||
|
this.scrollAnimationId = requestAnimationFrame(scrollAnimation);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检查浏览器是否支持 requestAnimationFrame
|
||||||
|
if (typeof requestAnimationFrame !== "undefined") {
|
||||||
|
this.scrollAnimationId = requestAnimationFrame(scrollAnimation);
|
||||||
|
} else {
|
||||||
|
// 回退到 setTimeout 方法
|
||||||
|
const scrollInterval = 16; // 约 60fps
|
||||||
|
const scrollStep = 16; // 每次滚动的距离
|
||||||
|
|
||||||
|
const scrollAnimationFallback = () => {
|
||||||
|
this.element.scroll({
|
||||||
|
top: this.element.scrollTop + scrollStep * scrollDirection
|
||||||
|
});
|
||||||
|
this.scrollAnimationId = window.setTimeout(scrollAnimationFallback, scrollInterval);
|
||||||
|
};
|
||||||
|
this.scrollAnimationId = window.setTimeout(scrollAnimationFallback, scrollInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 离开滚动区域时停止滚动
|
||||||
|
if (this.scrollAnimationId) {
|
||||||
|
if (typeof cancelAnimationFrame !== "undefined") {
|
||||||
|
cancelAnimationFrame(this.scrollAnimationId);
|
||||||
|
} else {
|
||||||
|
clearTimeout(this.scrollAnimationId);
|
||||||
|
}
|
||||||
|
this.scrollAnimationId = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
selectItem = hasClosestByClassName(moveEvent.target as HTMLElement, "b3-list-item") as HTMLElement;
|
selectItem = hasClosestByClassName(moveEvent.target as HTMLElement, "b3-list-item") as HTMLElement;
|
||||||
if (!selectItem || selectItem.tagName !== "LI" || selectItem.style.position === "fixed") {
|
if (!selectItem || selectItem.tagName !== "LI" || selectItem.style.position === "fixed") {
|
||||||
|
|
@ -410,6 +470,15 @@ export class Outline extends Model {
|
||||||
documentSelf.onselect = null;
|
documentSelf.onselect = null;
|
||||||
ghostElement?.remove();
|
ghostElement?.remove();
|
||||||
item.style.opacity = "";
|
item.style.opacity = "";
|
||||||
|
// 清理滚动动画
|
||||||
|
if (this.scrollAnimationId) {
|
||||||
|
if (typeof cancelAnimationFrame !== "undefined") {
|
||||||
|
cancelAnimationFrame(this.scrollAnimationId);
|
||||||
|
} else {
|
||||||
|
clearTimeout(this.scrollAnimationId);
|
||||||
|
}
|
||||||
|
this.scrollAnimationId = null;
|
||||||
|
}
|
||||||
if (!selectItem) {
|
if (!selectItem) {
|
||||||
selectItem = this.element.querySelector(".dragover__top, .dragover__bottom, .dragover");
|
selectItem = this.element.querySelector(".dragover__top, .dragover__bottom, .dragover");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue