Skip to content

Commit

Permalink
perf: ⚡ 优化卷轴模式性能
Browse files Browse the repository at this point in the history
  • Loading branch information
hymbz committed Jan 17, 2024
1 parent 5a85f53 commit d6923fc
Show file tree
Hide file tree
Showing 48 changed files with 1,046 additions and 804 deletions.
10 changes: 5 additions & 5 deletions .stylelintrc.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
module.exports = {
ignoreFiles: ['**/node_modules/**/*.css', '**/dist/**/*.css'],
extends: ['stylelint-config-standard', 'stylelint-config-clean-order'],
plugins: [
'stylelint-prettier',
'stylelint-order',
'stylelint-high-performance-animation',
extends: [
'stylelint-config-standard',
'stylelint-prettier/recommended',
'stylelint-config-clean-order',
],
plugins: ['stylelint-order', 'stylelint-high-performance-animation'],
rules: {
// 允许 css 变量使用任意命名方式
'custom-property-pattern': null,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
"dependencies": {
"@material-design-icons/svg": "^0.14.13",
"@placemarkio/flat-drop-files": "^1.0.2",
"@solid-primitives/scheduled": "^1.4.1",
"browser-fs-access": "^0.35.0",
"fast-deep-equal": "^3.1.3",
"fflate": "^0.8.1",
"jsencrypt": "^3.3.2",
"libarchive.js": "^1.3.0",
Expand All @@ -34,7 +36,6 @@
"pwa-install-handler": "^2.5.0",
"solid-element": "^1.8.0",
"solid-js": "^1.8.7",
"throttle-debounce": "^5.0.0",
"water.css": "^2.1.1"
},
"devDependencies": {
Expand All @@ -55,7 +56,6 @@
"@solidjs/router": "^0.10.5",
"@types/libarchive.js": "^1.3.4",
"@types/shelljs": "^0.8.15",
"@types/throttle-debounce": "^5.0.2",
"@types/wicg-file-system-access": "^2023.10.4",
"@types/wicg-web-app-launch": "^2023.1.3",
"@typescript-eslint/eslint-plugin": "^6.16.0",
Expand Down
30 changes: 14 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/components/Fab/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Component, JSX } from 'solid-js';
import { throttle } from 'helper';
import {
For,
onCleanup,
Expand All @@ -8,7 +9,6 @@ import {
mergeProps,
Show,
} from 'solid-js';
import { throttle } from 'throttle-debounce';

import MdMenuBook from '@material-design-icons/svg/round/menu_book.svg';
import classes, { css as style } from './index.module.css';
Expand Down Expand Up @@ -51,7 +51,7 @@ export const Fab: Component<FabProps> = (_props) => {
const [show, setShow] = createSignal(props.initialShow);

// 绑定滚动事件
const handleScroll = throttle(200, (e: Event) => {
const handleScroll = throttle((e: Event) => {
// 跳过非用户操作的滚动
if (e.isTrusted === false) return;
if (window.scrollY === lastY) return;
Expand All @@ -62,7 +62,7 @@ export const Fab: Component<FabProps> = (_props) => {
window.scrollY - lastY < 0,
);
lastY = window.scrollY;
});
}, 200);
onMount(() => window.addEventListener('scroll', handleScroll));
onCleanup(() => window.removeEventListener('scroll', handleScroll));

Expand Down
30 changes: 7 additions & 23 deletions src/components/Manga/actions/helper.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { scheduleIdle } from '@solid-primitives/scheduled';
import { difference, byPath } from 'helper';
import type { State } from '../store';
import { store, setState, refs } from '../store';
import type { Option } from '../store/option';
import { defaultOption } from '../store/option';

/** 触发 onOptionChange */
export const triggerOnOptionChange = () =>
setTimeout(
() => store.prop.OptionChange?.(difference(store.option, defaultOption)),
);
export const triggerOnOptionChange = scheduleIdle(
() => store.prop.OptionChange?.(difference(store.option, defaultOption)),
1000,
);

/** 在 option 后手动触发 onOptionChange */
export const setOption = (fn: (option: Option, state: State) => void) => {
Expand All @@ -35,22 +36,5 @@ export const resetUI = (state: State) => {
state.show.touchArea = false;
};

/** 检查已加载图片中是否**连续**出现了多个指定类型的图片 */
export const checkImgTypeCount = (
state: State,
fn: (img: ComicImg) => boolean,
maxNum = 3,
) => {
let num = 0;
for (let i = 0; i < state.imgList.length; i++) {
const img = state.imgList[i];
if (img.loadType !== 'loaded') continue;
if (!fn(img)) {
num = 0;
continue;
}
num += 1;
if (num >= maxNum) return true;
}
return false;
};
export const scrollTo = (top: number, smooth = false) =>
refs.mangaBox.scrollTo({ top, behavior: smooth ? 'smooth' : 'instant' });
26 changes: 10 additions & 16 deletions src/components/Manga/actions/hotkeys.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createMemo, createRoot } from 'solid-js';
import { isEqualArray } from 'helper';
import { isEqual } from 'helper';
import { createRootMemo } from '../helper';
import { _setState, store } from '../store';

export const defaultHotkeys: Readonly<Record<string, string[]>> = {
Expand All @@ -24,26 +24,20 @@ export const setHotkeys = (...args: any[]) => {
Object.fromEntries(
Object.entries(store.hotkeys).filter(
([name, keys]) =>
!defaultHotkeys[name] || !isEqualArray(keys, defaultHotkeys[name]),
!defaultHotkeys[name] || !isEqual(keys, defaultHotkeys[name]),
),
),
);
};

export const { hotkeysMap } = createRoot(() => {
const hotkeysMapMemo = createMemo(() =>
Object.fromEntries(
Object.entries(store.hotkeys).flatMap(([name, key]) =>
key.map((k) => [k, name]),
),
/** 快捷键配置 */
export const hotkeysMap = createRootMemo(() =>
Object.fromEntries(
Object.entries(store.hotkeys).flatMap(([name, key]) =>
key.map((k) => [k, name]),
),
);

return {
/** 快捷键配置 */
hotkeysMap: hotkeysMapMemo,
};
});
),
);

/** 删除指定快捷键 */
export const delHotkeys = (code: string) => {
Expand Down
79 changes: 19 additions & 60 deletions src/components/Manga/actions/image.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,9 @@
import { debounce } from 'throttle-debounce';
import { createMemo, createRoot } from 'solid-js';

import { clamp } from 'helper';
import {
autoCloseFill,
findFillIndex,
handleComicData,
} from '../handleComicData';
import { clamp, isEqual, debounce } from 'helper';
import { autoCloseFill, handleComicData } from '../handleComicData';
import type { State } from '../store';
import { store, setState, refs } from '../store';
import { contentHeight, updateDrag } from './scrollbar';
import { store } from '../store';
import { setOption } from './helper';

export const { activeImgIndex, nowFillIndex, activePage, preloadNum } =
createRoot(() => {
const activePageMemo = createMemo(
() => store.pageList[store.activePageIndex] ?? [],
);

const activeImgIndexMemo = createMemo(
() => activePageMemo().find((i) => i !== -1) ?? 0,
);

const nowFillIndexMemo = createMemo(() =>
findFillIndex(activeImgIndexMemo(), store.fillEffect),
);

const preloadNumMemo = createMemo(() => ({
back: store.option.preloadPageNum,
front: Math.floor(store.option.preloadPageNum / 2),
}));

return {
/** 当前显示的第一张图片的 index */
activeImgIndex: activeImgIndexMemo,
/** 当前所处的图片流 */
nowFillIndex: nowFillIndexMemo,
/** 当前显示页面 */
activePage: activePageMemo,
/** 预加载页数 */
preloadNum: preloadNumMemo,
};
});
import { activeImgIndex, preloadNum } from './memo';

type LoadImgDraft = { editNum: number; loadNum: number };
const loadImg = (state: State, index: number, draft: LoadImgDraft) => {
Expand Down Expand Up @@ -85,25 +47,19 @@ export const zoomScrollModeImg = (zoomLevel: number, set = false) => {
setOption((draftOption) => {
const newVal = set
? zoomLevel
: // 放大到整数再运算,避免精度丢失导致的奇怪的值
(store.option.scrollModeImgScale * 100 + zoomLevel * 100) / 100;

draftOption.scrollModeImgScale = clamp(0.1, newVal, 3);
});
// 在调整图片缩放后使当前滚动进度保持不变
refs.mangaFlow.scrollTo({
top: contentHeight() * store.scrollbar.dragTop,
behavior: 'instant',
: store.option.scrollModeImgScale + zoomLevel;
draftOption.scrollModeImgScale = clamp(0.1, +newVal.toFixed(2), 3);
});
setState(updateDrag);
};

/** 根据当前页数更新所有图片的加载状态 */
export const updateImgLoadType = debounce(100, (state: State) => {
export const updateImgLoadType = debounce((state: State) => {
// 先将所有加载中的图片状态改为暂停
state.imgList.forEach((img, i) => {
if (img.loadType === 'loading') state.imgList[i].loadType = 'wait';
});
let i = state.imgList.length;
while (i--) {
if (state.imgList[i].loadType === 'loading')
state.imgList[i].loadType = 'wait';
}

return (
// 优先加载当前显示页
Expand Down Expand Up @@ -131,10 +87,12 @@ export const updatePageData = (state: State) => {
isMobile,
} = state;

let newPageList: PageList = [];
if (onePageMode || scrollMode || isMobile || imgList.length <= 1)
state.pageList = imgList.map((_, i) => [i]);
else state.pageList = handleComicData(imgList, fillEffect);
updateDrag(state);
newPageList = imgList.map((_, i) => [i]);
else newPageList = handleComicData(imgList, fillEffect);
if (!isEqual(state.pageList, newPageList)) state.pageList = newPageList;

updateImgLoadType(state);

// 在图片排列改变后自动跳转回原先显示图片所在的页数
Expand All @@ -153,8 +111,9 @@ export const updatePageData = (state: State) => {
* 3. updatePageData
*/
export const resetImgState = (state: State) => {
state.flag.autoScrollMode = true;
state.flag.autoScrollMode = false;
state.flag.autoWide = false;
state.flag.autoLong = false;
autoCloseFill.clear();
// 如果用户没有手动修改过首页填充,才将其恢复初始
if (typeof state.fillEffect['-1'] === 'boolean')
Expand Down
Loading

0 comments on commit d6923fc

Please sign in to comment.