Skip to content

Commit

Permalink
fix: 🐛 修复切换页面填充会导致图片重新加载的 bug
Browse files Browse the repository at this point in the history
  • Loading branch information
hymbz committed Jan 31, 2024
1 parent d5ff635 commit a3795fe
Show file tree
Hide file tree
Showing 27 changed files with 312 additions and 231 deletions.
2 changes: 1 addition & 1 deletion docs/.other/Dev.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## TODO

- 在卷轴模式下实现自己的手势滚动,以便:在卷轴模式下使用鼠标拖拽滚动、在卷轴模式下缩放拖拽到尽头后能够直接开始页面滚动
- 在卷轴模式下使用鼠标拖拽滚动

## 调试

Expand Down
2 changes: 1 addition & 1 deletion src/components/Manga/actions/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { Option } from '../store/option';
import { defaultOption } from '../store/option';

/** 触发 onOptionChange */
export const triggerOnOptionChange = scheduleIdle(
const triggerOnOptionChange = scheduleIdle(
() => store.prop.OptionChange?.(difference(store.option, defaultOption)),
1000,
);
Expand Down
20 changes: 9 additions & 11 deletions src/components/Manga/actions/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { autoCloseFill, handleComicData } from '../handleComicData';
import type { State } from '../store';
import { store } from '../store';
import { scrollTo, setOption } from './helper';
import { activeImgIndex, contentHeight, preloadNum, scrollTop } from './memo';
import {
activeImgIndex,
contentHeight,
isOnePageMode,
preloadNum,
scrollTop,
} from './memo';

type LoadImgDraft = { editNum: number; loadNum: number };
const loadImg = (state: State, index: number, draft: LoadImgDraft) => {
Expand Down Expand Up @@ -86,19 +92,11 @@ export const updateImgLoadType = debounce((state: State) => {
/** 重新计算 PageData */
export const updatePageData = (state: State) => {
const lastActiveImgIndex = activeImgIndex();
const {
imgList,
fillEffect,
option: { onePageMode, scrollMode },
isMobile,
} = state;

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

updateImgLoadType(state);

// 在图片排列改变后自动跳转回原先显示图片所在的页数
Expand Down
21 changes: 16 additions & 5 deletions src/components/Manga/actions/memo/common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { createRootMemo, createThrottleMemo } from 'helper/solidJs';
import { store, refs } from '../../store';
import { store } from '../../store';
import { findFillIndex } from '../../handleComicData';
import { rootSize } from './observer';

/** 是否为单页模式 */
export const isOnePageMode = createRootMemo(
() =>
store.option.onePageMode ||
store.option.scrollMode ||
store.isMobile ||
store.imgList.length <= 1,
);

/** 当前显示页面 */
export const activePage = createRootMemo(
Expand Down Expand Up @@ -32,7 +42,6 @@ export const defaultImgType = createRootMemo<ComicImg['type']>(() => {

/** 获取图片列表中指定属性的中位数 */
const getImgMedian = (sizeFn: (value: ComicImg) => number) => {
if (!store.option.scrollMode) return 0;
const list = store.imgList
.filter((img) => img.loadType === 'loaded' && img.width)
.map(sizeFn)
Expand All @@ -44,8 +53,8 @@ const getImgMedian = (sizeFn: (value: ComicImg) => number) => {
/** 图片占位尺寸 */
export const placeholderSize = createThrottleMemo(
() => ({
width: getImgMedian((img) => img.width!) ?? refs.root?.offsetWidth,
height: getImgMedian((img) => img.height!) ?? refs.root?.offsetHeight,
width: getImgMedian((img) => img.width!) ?? 800,
height: getImgMedian((img) => img.height!) ?? 600,
}),
500,
);
Expand All @@ -55,7 +64,9 @@ export const imgHeightList = createRootMemo(() =>
store.option.scrollMode
? store.imgList.map(
(img) =>
(img.height ?? placeholderSize().height) *
(img.height && img.width && img.width > rootSize().width
? img.height * (rootSize().width / img.width)
: img.height ?? placeholderSize().height) *
store.option.scrollModeImgScale,
)
: [],
Expand Down
27 changes: 19 additions & 8 deletions src/components/Manga/actions/memo/observer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
import { createRoot, createSignal, onCleanup } from 'solid-js';
import { inRange, throttle } from 'helper';
import { createEffectOn, createEqualsSignal } from 'helper/solidJs';
import {
createEffectOn,
createEqualsSignal,
createRootMemo,
} from 'helper/solidJs';
import { store, _setState, setState } from '../../store';

/** 记录每张图片所在的页面 */
export const imgPageMap = createRootMemo(() => {
const map: Record<number, number> = {};
for (let i = 0; i < store.pageList.length; i++) {
store.pageList[i].forEach((imgIndex) => {
if (imgIndex !== -1) map[imgIndex] = i;
});
}
return map;
});

/** 当前显示的图片 */
export const showImgList = new Set<HTMLImageElement>();

Expand All @@ -11,9 +26,7 @@ const [_showPageList, setShowPageList] = createEqualsSignal<number[]>([]);
export const showPageList = _showPageList;
const updateShowPageList = throttle(() => {
const newShowPageList = new Set<number>();
showImgList.forEach((img) =>
newShowPageList.add(+img.parentElement!.getAttribute('data-index')!),
);
showImgList.forEach((img) => newShowPageList.add(imgPageMap()[+img.alt]));
setShowPageList([...newShowPageList].sort((a, b) => a - b));
});

Expand Down Expand Up @@ -50,10 +63,8 @@ export const rootSize = _rootSize;
export const initResizeObserver = (dom: HTMLElement) => {
setRootSize({ width: dom.scrollWidth, height: dom.scrollHeight });
// 在 rootDom 的大小改变时更新比例,并重新计算图片类型
const resizeObserver = new ResizeObserver(
throttle(([{ contentRect }]) =>
setRootSize({ width: contentRect.width, height: contentRect.height }),
),
const resizeObserver = new ResizeObserver(([{ contentRect }]) =>
setRootSize({ width: contentRect.width, height: contentRect.height }),
);
resizeObserver.disconnect();
resizeObserver.observe(dom);
Expand Down
36 changes: 33 additions & 3 deletions src/components/Manga/actions/memo/renderPage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createRoot, createSignal } from 'solid-js';
import { inRange } from 'helper';
import { createEffectOn } from 'helper/solidJs';
import { createEffectOn, createRootMemo } from 'helper/solidJs';
import type { State } from '../../store';
import { setState, store } from '../../store';
import { contentHeight, imgTopList } from './common';
Expand All @@ -9,7 +9,7 @@ import { rootSize, scrollTop } from './observer';
const [renderRangeStart, setRenderRangeStart] = createSignal(0);
const [renderRangeEnd, setRenderRangeEnd] = createSignal(0);

/** 渲染范围 */
/** 渲染页面的范围 */
export const renderRange = { start: renderRangeStart, end: renderRangeEnd };

const findTopImg = (initIndex: number, top: number) => {
Expand Down Expand Up @@ -38,7 +38,7 @@ export const updateRenderRange = (state: State) => {
}
} else {
startPage = Math.max(0, state.activePageIndex - 1);
endPage = Math.min(state.pageList.length, state.activePageIndex + 2);
endPage = Math.min(state.pageList.length - 1, state.activePageIndex + 2);
}

if (!startPage) startPage = 0;
Expand All @@ -65,3 +65,33 @@ createRoot(() => {
endImgTop = imgTopList()[renderRangeEnd()];
});
});

/** 渲染图片的范围 */
export const renderImgRange = createRootMemo(() => {
if (!store.pageList[renderRangeStart()] || !store.pageList[renderRangeEnd()])
return { start: 0, end: 0 };
const renderImgList = [
...store.pageList[renderRangeStart()],
...store.pageList[renderRangeEnd()],
].filter((i) => i !== -1);
return { start: Math.min(...renderImgList), end: Math.max(...renderImgList) };
});

/**
* 图片显示状态
*
* 0 - 页面中的第一张图片
* 1 - 页面中的最后一张图片
* 2 - 页面中的唯一一张图片
*/
export const imgShowState = createRootMemo<(0 | 1 | 2)[]>(() => {
const stateList: (0 | 1 | 2)[] = [];
for (let i = 0; i < store.pageList.length; i++) {
const [a, b] = store.pageList[i];
if (b !== undefined) {
stateList[a] = 0;
stateList[b] = 1;
} else stateList[a] = 2;
}
return stateList;
}, []);
4 changes: 2 additions & 2 deletions src/components/Manga/actions/operate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import { rootSize, scrollTop } from './memo';
// 特意使用 requestAnimationFrame 和 .click() 是为了能和 Vimium 兼容
export const focus = () =>
requestAnimationFrame(() => {
refs.mangaFlow?.click();
refs.mangaFlow?.focus();
refs.mangaBox?.click();
refs.mangaBox?.focus();
});

export const handleMouseDown: EventHandler['on:mousedown'] = (e) => {
Expand Down
15 changes: 8 additions & 7 deletions src/components/Manga/actions/pointer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ import { useDoubleClick } from '../hooks/useDoubleClick';
import type { UseDrag } from '../hooks/useDrag';
import { store, setState, refs } from '../store';
import { resetUI, scrollTo } from './helper';
import { imgPageMap, imgTopList, rootSize } from './memo';
import { resetPage } from './show';
import { zoom } from './zoom';
import {
turnPageFn,
turnPageAnimation,
turnPage,
isBottom,
isTop,
} from './turnPage';
import { zoom } from './zoom';
import { imgTopList, rootSize } from './memo';

/** 根据坐标判断点击的元素 */
const findClickEle = (eleList: HTMLCollection, { x, y }: MouseEvent) =>
const findClickEle = <T extends Element>(
eleList: HTMLCollectionOf<T>,
{ x, y }: MouseEvent,
) =>
[...eleList].find((e) => {
const rect = e.getBoundingClientRect();
return (
Expand Down Expand Up @@ -48,10 +51,8 @@ export const handlePageClick = (e: MouseEvent) => {
export const handleGridClick = (e: MouseEvent) => {
const target = findClickEle(refs.root.getElementsByTagName('img'), e);
if (!target) return;
const pageNumText = target.parentElement?.getAttribute('data-index');
if (!pageNumText) return;
const pageNum = +pageNumText;
if (!Reflect.has(store.pageList, pageNum)) return;
const pageNum = imgPageMap()[+target.alt];
if (pageNum === undefined) return;
setState((state) => {
state.activePageIndex = pageNum;
state.gridMode = false;
Expand Down
13 changes: 7 additions & 6 deletions src/components/Manga/actions/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,27 @@ import { activePage, renderRange, updateRenderRange } from './memo';
/** 将页面移回原位 */
export const resetPage = (state: State, animation = false) => {
updateRenderRange(state);
state.page.offset.x.pct = 0;
state.page.offset.y.pct = 0;

if (state.option.scrollMode) {
state.page.anima = '';
return;
}

state.page.offset.x.pct = 0;
state.page.offset.y.pct = 0;
let i = -1;
if (inRange(renderRange.start(), state.activePageIndex, renderRange.end()))
i = state.activePageIndex - renderRange.start();
if (store.page.vertical) state.page.offset.y.pct = i === -1 ? 0 : -i * 100;
else state.page.offset.x.pct = i === -1 ? 0 : i * 100;
else state.page.offset.x.pct = i === -1 ? 0 : i;

state.page.anima = animation ? 'page' : '';
};

/** 获取指定图片的提示文本 */
const getImgTip = (state: State, i: number) => {
export const getImgTip = (i: number) => {
if (i === -1) return t('other.fill_page');
const img = state.imgList[i];
const img = store.imgList[i];

// 如果图片未加载完毕则在其 index 后增加显示当前加载状态
if (img.loadType !== 'loaded')
Expand All @@ -50,7 +51,7 @@ const getImgTip = (state: State, i: number) => {
export const getPageTip = (pageIndex: number): string => {
const page = store.pageList[pageIndex];
if (!page) return 'null';
const pageIndexText = page.map((index) => getImgTip(store, index)) as
const pageIndexText = page.map((index) => getImgTip(index)) as
| [string]
| [string, string];
if (store.option.dir === 'rtl') pageIndexText.reverse();
Expand Down
16 changes: 9 additions & 7 deletions src/components/Manga/actions/switch.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { refs, setState, store } from '../store';
import { zoom } from './zoom';
import { setOption } from './helper';
import { scrollTo, setOption } from './helper';
import { updatePageData } from './image';
import { nowFillIndex } from './memo';
import { activeImgIndex, imgTopList, nowFillIndex } from './memo';

/** 切换页面填充 */
export const switchFillEffect = () => {
Expand All @@ -24,8 +24,7 @@ export const switchScrollMode = () => {
updatePageData(state);
});
// 切换到卷轴模式后自动定位到对应页
if (store.option.scrollMode)
refs.mangaFlow.children[store.activePageIndex]?.scrollIntoView();
if (store.option.scrollMode) scrollTo(imgTopList()[store.activePageIndex]);
};

/** 切换单双页模式 */
Expand All @@ -45,15 +44,18 @@ export const switchDir = () => {

/** 切换网格模式 */
export const switchGridMode = () => {
zoom(100);
setState((state) => {
state.gridMode = !state.gridMode;
if (state.zoom.scale !== 100) zoom(100);
state.page.anima = '';
});
// 切换到网格模式后自动定位到当前页
if (store.gridMode)
refs.mangaFlow.children[store.activePageIndex]?.scrollIntoView({
block: 'center',
inline: 'center',
requestAnimationFrame(() => {
refs.mangaFlow.children[activeImgIndex()]?.scrollIntoView({
block: 'center',
inline: 'center',
});
});
};
8 changes: 5 additions & 3 deletions src/components/Manga/actions/translation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ export const translationImage = async (i: number) => {
if (img.loadType !== 'loaded')
return setMessage(i, t('translation.tip.img_not_fully_loaded'));

const translationUrl = await (store.option.translation.server === 'cotrans'
? cotransTranslation
: selfhostedTranslation)(i);
const translationUrl = await (
store.option.translation.server === 'cotrans'
? cotransTranslation
: selfhostedTranslation
)(i);

setState((state) => {
state.imgList[i].translationUrl = translationUrl;
Expand Down
5 changes: 2 additions & 3 deletions src/components/Manga/actions/turnPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,8 @@ export const turnPageAnimation = (dir: 'next' | 'prev') => {

state.isDragMode = true;
resetPage(state);
if (store.page.vertical)
state.page.offset.y.pct += dir === 'next' ? 100 : -100;
else state.page.offset.x.pct += dir === 'next' ? -100 : 100;
if (store.page.vertical) state.page.offset.y.pct += dir === 'next' ? 1 : -1;
else state.page.offset.x.pct += dir === 'next' ? -1 : 1;

setTimeout(() => {
setState((draftState) => {
Expand Down
Loading

0 comments on commit a3795fe

Please sign in to comment.