summaryrefslogtreecommitdiff
path: root/packages/client/src
diff options
context:
space:
mode:
authortamaina <tamaina@hotmail.co.jp>2022-02-06 10:59:36 +0900
committerGitHub <noreply@github.com>2022-02-06 10:59:36 +0900
commit6cbd66b5344ae37e70cb712b5ebc55547b02074a (patch)
tree714c5edc1da93d9c227ab2cce374418da591f949 /packages/client/src
parentUpdate dev.js (diff)
downloadmisskey-6cbd66b5344ae37e70cb712b5ebc55547b02074a.tar.gz
misskey-6cbd66b5344ae37e70cb712b5ebc55547b02074a.tar.bz2
misskey-6cbd66b5344ae37e70cb712b5ebc55547b02074a.zip
fix: v-sizeディレクティブの動作を修正 (#8249)
* Fix size directive behavior not activated * calc * wip * cache computed classes * fix Vue3では使えなくなった * 不要なIntersection Observerを削除 * comment
Diffstat (limited to 'packages/client/src')
-rw-r--r--packages/client/src/directives/get-size.ts61
-rw-r--r--packages/client/src/directives/size.ts137
2 files changed, 129 insertions, 69 deletions
diff --git a/packages/client/src/directives/get-size.ts b/packages/client/src/directives/get-size.ts
index e3b5dea0f3..1fcd0718dc 100644
--- a/packages/client/src/directives/get-size.ts
+++ b/packages/client/src/directives/get-size.ts
@@ -1,34 +1,55 @@
import { Directive } from 'vue';
-export default {
- mounted(src, binding, vn) {
- const calc = () => {
- const height = src.clientHeight;
- const width = src.clientWidth;
+const mountings = new Map<Element, {
+ resize: ResizeObserver;
+ intersection?: IntersectionObserver;
+ fn: (w: number, h: number) => void;
+}>();
+
+function calc(src: Element) {
+ const info = mountings.get(src);
+ const height = src.clientHeight;
+ const width = src.clientWidth;
- // 要素が(一時的に)DOMに存在しないときは計算スキップ
- if (height === 0) return;
+ if (!info) return;
- binding.value(width, height);
- };
+ // アクティベート前などでsrcが描画されていない場合
+ if (!height) {
+ // IntersectionObserverで表示検出する
+ if (!info.intersection) {
+ info.intersection = new IntersectionObserver(entries => {
+ if (entries.some(entry => entry.isIntersecting)) calc(src);
+ });
+ }
+ info.intersection.observe(src);
+ return;
+ }
+ if (info.intersection) {
+ info.intersection.disconnect()
+ delete info.intersection;
+ };
- calc();
+ info.fn(width, height);
+};
- // Vue3では使えなくなった
- // 無くても大丈夫か...?
- // TODO: ↑大丈夫じゃなかったので解決策を探す
- //vn.context.$on('hook:activated', calc);
+export default {
+ mounted(src, binding, vn) {
- const ro = new ResizeObserver((entries, observer) => {
- calc();
+ const resize = new ResizeObserver((entries, observer) => {
+ calc(src);
});
- ro.observe(src);
+ resize.observe(src);
- src._get_size_ro_ = ro;
+ mountings.set(src, { resize, fn: binding.value, });
+ calc(src);
},
unmounted(src, binding, vn) {
binding.value(0, 0);
- src._get_size_ro_.unobserve(src);
+ const info = mountings.get(src);
+ if (!info) return;
+ info.resize.disconnect();
+ if (info.intersection) info.intersection.disconnect();
+ mountings.delete(src);
}
-} as Directive;
+} as Directive<Element, (w: number, h: number) => void>;
diff --git a/packages/client/src/directives/size.ts b/packages/client/src/directives/size.ts
index a72a97abcc..36f649f180 100644
--- a/packages/client/src/directives/size.ts
+++ b/packages/client/src/directives/size.ts
@@ -1,68 +1,107 @@
import { Directive } from 'vue';
+type Value = { max?: number[]; min?: number[]; };
+
//const observers = new Map<Element, ResizeObserver>();
+const mountings = new Map<Element, {
+ value: Value;
+ resize: ResizeObserver;
+ intersection?: IntersectionObserver;
+ previousWidth: number;
+}>();
-export default {
- mounted(src, binding, vn) {
- const query = binding.value;
+type ClassOrder = {
+ add: string[];
+ remove: string[];
+};
- const addClass = (el: Element, cls: string) => {
- el.classList.add(cls);
- };
+const cache = new Map<string, ClassOrder>();
- const removeClass = (el: Element, cls: string) => {
- el.classList.remove(cls);
- };
+function getClassOrder(width: number, queue: Value): ClassOrder {
+ const getMaxClass = (v: number) => `max-width_${v}px`;
+ const getMinClass = (v: number) => `min-width_${v}px`;
- const calc = () => {
- const width = src.clientWidth;
+ return {
+ add: [
+ ...(queue.max ? queue.max.filter(v => width <= v).map(getMaxClass) : []),
+ ...(queue.min ? queue.min.filter(v => width >= v).map(getMinClass) : []),
+ ],
+ remove: [
+ ...(queue.max ? queue.max.filter(v => width > v).map(getMaxClass) : []),
+ ...(queue.min ? queue.min.filter(v => width < v).map(getMinClass) : []),
+ ]
+ };
+}
- // 要素が(一時的に)DOMに存在しないときは計算スキップ
- if (width === 0) return;
+function applyClassOrder(el: Element, order: ClassOrder) {
+ el.classList.add(...order.add);
+ el.classList.remove(...order.remove);
+}
- if (query.max) {
- for (const v of query.max) {
- if (width <= v) {
- addClass(src, 'max-width_' + v + 'px');
- } else {
- removeClass(src, 'max-width_' + v + 'px');
- }
- }
- }
- if (query.min) {
- for (const v of query.min) {
- if (width >= v) {
- addClass(src, 'min-width_' + v + 'px');
- } else {
- removeClass(src, 'min-width_' + v + 'px');
- }
- }
- }
- };
+function getOrderName(width: number, queue: Value): string {
+ return `${width}|${queue.max ? queue.max.join(',') : ''}|${queue.min ? queue.min.join(',') : ''}`;
+}
- calc();
+function calc(el: Element) {
+ const info = mountings.get(el);
+ const width = el.clientWidth;
- window.addEventListener('resize', calc);
+ if (!info || info.previousWidth === width) return;
- // Vue3では使えなくなった
- // 無くても大丈夫か...?
- // TODO: ↑大丈夫じゃなかったので解決策を探す
- //vn.context.$on('hook:activated', calc);
+ // アクティベート前などでsrcが描画されていない場合
+ if (!width) {
+ // IntersectionObserverで表示検出する
+ if (!info.intersection) {
+ info.intersection = new IntersectionObserver(entries => {
+ if (entries.some(entry => entry.isIntersecting)) calc(el);
+ });
+ }
+ info.intersection.observe(el);
+ return;
+ }
+ if (info.intersection) {
+ info.intersection.disconnect()
+ delete info.intersection;
+ };
+
+ mountings.set(el, Object.assign(info, { previousWidth: width }));
- //const ro = new ResizeObserver((entries, observer) => {
- // calc();
- //});
+ const cached = cache.get(getOrderName(width, info.value));
+ if (cached) {
+ applyClassOrder(el, cached);
+ } else {
+ const order = getClassOrder(width, info.value);
+ cache.set(getOrderName(width, info.value), order);
+ applyClassOrder(el, order);
+ }
+}
- //ro.observe(el);
+export default {
+ mounted(src, binding, vn) {
+ const resize = new ResizeObserver((entries, observer) => {
+ calc(src);
+ });
+
+ mountings.set(src, {
+ value: binding.value,
+ resize,
+ previousWidth: 0,
+ });
+
+ calc(src);
+ resize.observe(src);
+ },
- // TODO: 新たにプロパティを作るのをやめMapを使う
- // ただメモリ的には↓の方が省メモリかもしれないので検討中
- //el._ro_ = ro;
- src._calc_ = calc;
+ updated(src, binding, vn) {
+ mountings.set(src, Object.assign({}, mountings.get(src), { value: binding.value }));
+ calc(src);
},
unmounted(src, binding, vn) {
- //el._ro_.unobserve(el);
- window.removeEventListener('resize', src._calc_);
+ const info = mountings.get(src);
+ if (!info) return;
+ info.resize.disconnect();
+ if (info.intersection) info.intersection.disconnect();
+ mountings.delete(src);
}
-} as Directive;
+} as Directive<Element, Value>;