Improve perf and way items are synchronized
This commit is contained in:
parent
31f6c2807c
commit
0e087ed1c3
7 changed files with 86 additions and 63 deletions
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -8540,6 +8540,11 @@
|
|||
"lodash._reinterpolate": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
||||
},
|
||||
"lodash.uniq": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"cors": "^2.8.5",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.findlast": "^4.6.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"memoizee": "^0.4.14",
|
||||
"nanoid": "^3.1.9",
|
||||
"randomcolor": "^0.5.4",
|
||||
|
|
|
@ -5,8 +5,10 @@ import { selectedItemsAtom } from '../components/Selector';
|
|||
import { ItemListAtom } from '../components/Items';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { insideClass, isPointInsideRect, isPointInsideItem } from '../utils';
|
||||
import { useC2C } from '../hooks/useC2C';
|
||||
|
||||
const ActionPane = ({ children }) => {
|
||||
const [c2c] = useC2C();
|
||||
const panZoomRotate = useRecoilValue(PanZoomRotateState);
|
||||
const [itemList, setItemList] = useRecoilState(ItemListAtom);
|
||||
const [selectedItems, setSelectedItems] = useRecoilState(selectedItemsAtom);
|
||||
|
@ -45,17 +47,20 @@ const ActionPane = ({ children }) => {
|
|||
ids = selectedItems;
|
||||
}
|
||||
setItemList((prevList) => {
|
||||
return prevList.map((item) => {
|
||||
const newItemList = prevList.map((item) => {
|
||||
if (ids.includes(item.id)) {
|
||||
const x = item.x + posDelta.x;
|
||||
const y = item.y + posDelta.y;
|
||||
return { ...item, x, y };
|
||||
const newItem = { ...item, x, y };
|
||||
return newItem;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
c2c.publish(`selectedItemsMove`, { itemIds: ids, move: posDelta });
|
||||
return newItemList;
|
||||
});
|
||||
},
|
||||
[setItemList, selectedItems]
|
||||
[setItemList, selectedItems, c2c]
|
||||
);
|
||||
|
||||
const onMouseMouve = (e) => {
|
||||
|
@ -80,6 +85,23 @@ const ActionPane = ({ children }) => {
|
|||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const unsub = c2c.subscribe(`selectedItemsMove`, ({ itemIds, move }) => {
|
||||
setItemList((prevList) => {
|
||||
return prevList.map((item) => {
|
||||
if (itemIds.includes(item.id)) {
|
||||
const x = item.x + move.x;
|
||||
const y = item.y + move.y;
|
||||
const newItem = { ...item, x, y };
|
||||
return newItem;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
});
|
||||
});
|
||||
return unsub;
|
||||
}, [c2c, setItemList]);
|
||||
|
||||
return (
|
||||
<div
|
||||
onMouseDown={onMouseDown}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import React from 'react';
|
||||
import { DraggableCore } from 'react-draggable';
|
||||
import { useC2C } from '../hooks/useC2C';
|
||||
import { PanZoomRotateState } from '../components/PanZoomRotate';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { selectedItemsAtom } from './Selector';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
const Rect = ({ width, height, color }) => {
|
||||
return (
|
||||
|
@ -50,7 +48,7 @@ const Image = ({
|
|||
}
|
||||
|
||||
const onDblClick = (e) => {
|
||||
updateState({ flipped: !flipped });
|
||||
updateState((prevItem) => ({ ...prevItem, flipped: !prevItem.flipped }));
|
||||
};
|
||||
|
||||
if (flipped && backContent) {
|
||||
|
@ -134,8 +132,8 @@ const Item = ({ setState, state }) => {
|
|||
const rotation = state.rotation || 0;
|
||||
|
||||
const updateState = React.useCallback(
|
||||
(modif) => {
|
||||
setState({ ...state, ...modif });
|
||||
(callbackOrItem) => {
|
||||
setState(state.id, callbackOrItem);
|
||||
},
|
||||
[setState, state]
|
||||
);
|
||||
|
@ -148,11 +146,11 @@ const Item = ({ setState, state }) => {
|
|||
if (entry.contentBoxSize) {
|
||||
const { inlineSize: width, blockSize: height } = entry.contentBoxSize;
|
||||
if (state.actualWidth !== width || state.actualHeight !== height) {
|
||||
setState({
|
||||
...state,
|
||||
updateState((prevState) => ({
|
||||
...prevState,
|
||||
actualWidth: width,
|
||||
actualHeight: height,
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -162,7 +160,7 @@ const Item = ({ setState, state }) => {
|
|||
return () => {
|
||||
observer.unobserve(currentElem);
|
||||
};
|
||||
}, [setState, state]);
|
||||
}, [updateState, state]);
|
||||
|
||||
const content = (
|
||||
<div
|
||||
|
@ -200,27 +198,12 @@ const Item = ({ setState, state }) => {
|
|||
|
||||
const SyncedItem = ({ setState, state }) => {
|
||||
const [c2c] = useC2C();
|
||||
const versionsRef = React.useRef([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (versionsRef.current.includes(state.version)) {
|
||||
versionsRef.current = versionsRef.current.filter((v) => {
|
||||
return v !== state.version;
|
||||
});
|
||||
} else {
|
||||
c2c.publish(`itemStateUpdate.${state.id}`, {
|
||||
...state,
|
||||
});
|
||||
}
|
||||
}, [c2c, setState, state]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const unsub = c2c.subscribe(
|
||||
`itemStateUpdate.${state.id}`,
|
||||
(newItemState) => {
|
||||
const nextVersion = nanoid();
|
||||
versionsRef.current.push(nextVersion);
|
||||
setState({ ...newItemState, version: nextVersion });
|
||||
setState(state.id, newItemState, false);
|
||||
}
|
||||
);
|
||||
return unsub;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { useRecoilState, atom, selector, useRecoilValue } from 'recoil';
|
||||
import { useRecoilState, atom } from 'recoil';
|
||||
import Item from '../components/Item';
|
||||
import { selectedItemsAtom } from './Selector';
|
||||
import { useC2C } from '../hooks/useC2C';
|
||||
|
||||
export const ItemListAtom = atom({
|
||||
key: 'itemList',
|
||||
|
@ -9,24 +9,34 @@ export const ItemListAtom = atom({
|
|||
});
|
||||
|
||||
const Items = ({}) => {
|
||||
const [c2c] = useC2C();
|
||||
const [itemList, setItemList] = useRecoilState(ItemListAtom);
|
||||
|
||||
const updateItemState = React.useCallback(
|
||||
(newState) => {
|
||||
const updateItem = React.useCallback(
|
||||
(id, callbackOrItem, sync = true) => {
|
||||
let callback = callbackOrItem;
|
||||
if (typeof callbackOrItem === 'object') {
|
||||
callback = (item) => callbackOrItem;
|
||||
}
|
||||
setItemList((prevList) => {
|
||||
return prevList.map((item) => {
|
||||
if (item.id === newState.id) {
|
||||
return { ...item, ...newState, id: item.id };
|
||||
if (item.id === id) {
|
||||
const newItem = {
|
||||
...callback(item),
|
||||
id: item.id, // Prevent id modification
|
||||
};
|
||||
sync && c2c.publish(`itemStateUpdate.${id}`, newItem);
|
||||
return newItem;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
});
|
||||
},
|
||||
[setItemList]
|
||||
[setItemList, c2c]
|
||||
);
|
||||
|
||||
return itemList.map((item) => (
|
||||
<Item key={item.id} state={item} setState={updateItemState} />
|
||||
<Item key={item.id} state={item} setState={updateItem} />
|
||||
));
|
||||
};
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ const Selector = ({ children }) => {
|
|||
const selected = findSelected(itemList, stateRef.current).map(
|
||||
({ id }) => id
|
||||
);
|
||||
setSelected(findSelected(itemList, stateRef.current).map(({ id }) => id));
|
||||
setSelected(selected);
|
||||
stateRef.current = { moving: false };
|
||||
setSelector({ ...stateRef.current });
|
||||
wrapperRef.current.style.cursor = 'auto';
|
||||
|
|
|
@ -3,34 +3,36 @@ import { urlAlphabet } from 'nanoid';
|
|||
const genGame = () => {
|
||||
const items = [];
|
||||
|
||||
items.push({
|
||||
type: 'image',
|
||||
content: '/games/card.jpg',
|
||||
backContent: '/games/back.jpg',
|
||||
width: 100,
|
||||
flipped: true,
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
[...Array(10).keys()].forEach(() => {
|
||||
items.push({
|
||||
type: 'image',
|
||||
content: '/games/card.jpg',
|
||||
backContent: '/games/back.jpg',
|
||||
width: 100,
|
||||
flipped: true,
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
|
||||
items.push({
|
||||
type: 'image',
|
||||
content: '/games/card.jpg',
|
||||
backContent: '/games/back.jpg',
|
||||
width: 100,
|
||||
flipped: true,
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
items.push({
|
||||
type: 'image',
|
||||
content: '/games/card.jpg',
|
||||
backContent: '/games/back.jpg',
|
||||
width: 100,
|
||||
flipped: true,
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
|
||||
items.push({
|
||||
type: 'image',
|
||||
content: '/games/8diamond.png',
|
||||
backContent: '/games/back.jpg',
|
||||
width: 100,
|
||||
flipped: true,
|
||||
x: 100,
|
||||
y: 100,
|
||||
items.push({
|
||||
type: 'image',
|
||||
content: '/games/8diamond.png',
|
||||
backContent: '/games/back.jpg',
|
||||
width: 100,
|
||||
flipped: true,
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
Loading…
Reference in a new issue