update 0.8.1

This commit is contained in:
hufeixiong
2021-08-11 17:16:22 +08:00
parent 2a2b6306b8
commit e1380a2e3f
13 changed files with 474 additions and 22 deletions

View File

@@ -1,5 +1,9 @@
## changelog
## 0.8.1
新增动画组件timeline。可以更好预览所有动画。
## 0.8.0
动画部分重构,可支持多动画同时配置。

View File

@@ -4,6 +4,10 @@ order: 1
---
## changelog
## 0.8.1
新增动画组件timeline。可以更好预览所有动画。
## 0.8.0
动画部分重构,可支持多动画同时配置。

View File

@@ -2,7 +2,7 @@
* @Author: yehuozhili
* @Date: 2021-05-15 12:49:28
* @LastEditors: yehuozhili
* @LastEditTime: 2021-07-20 16:30:12
* @LastEditTime: 2021-08-11 16:26:46
* @FilePath: \dooringx\packages\dooringx-example\src\pages\index.tsx
*/
import {
@@ -31,7 +31,6 @@ export default function IndexPage() {
}, [config]);
const [state] = useStoreState(config, subscribeFn, everyFn);
return (
<div {...innerContainerDragUp(config)}>
<div style={{ height: HeaderHeight }}>
@@ -68,7 +67,7 @@ export default function IndexPage() {
<>
<Control
config={config}
style={{ position: 'fixed', bottom: '60px', right: '450px', zIndex: 100 }}
style={{ position: 'fixed', bottom: '160px', right: '450px', zIndex: 100 }}
></Control>
<Container state={state} config={config} context="edit"></Container>
</>

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react';
import React, { useMemo } from 'react';
import { UserConfig, deepCopy, createUid } from 'dooringx-lib';
import { Col, Row, Select, InputNumber, Button } from 'antd';
import { FormMap, FormBaseType } from '../formTypes';
@@ -121,9 +121,19 @@ const timeFunction: Record<string, string> = {
: 'ease in',
};
let lastAnimate: AnimateItem[] = [];
let isOmit = false;
function AnimateControl(props: AnimateControlProps) {
const animate = props.current.animate;
const store = props.config.getStore();
const animate = useMemo(() => {
if (isOmit) {
return lastAnimate;
}
lastAnimate = props.current.animate;
return props.current.animate;
}, [props.current.animate]);
return (
<>
{animate.map((v, i) => {
@@ -164,6 +174,7 @@ function AnimateControl(props: AnimateControlProps) {
<Col span={7}>
<InputNumber
style={{ width: '100%' }}
step="0.1"
value={animate[i].animationDuration}
formatter={(value) => `${value}s`}
min={0}
@@ -191,6 +202,7 @@ function AnimateControl(props: AnimateControlProps) {
value={animate[i].animationDelay}
formatter={(value) => `${value}s`}
min={0}
step="0.1"
onChange={(d) => {
const cloneData: IStoreData = deepCopy(store.getData());
cloneData.block.forEach((w) => {
@@ -291,22 +303,28 @@ function AnimateControl(props: AnimateControlProps) {
{animate.length > 0 && (
<Button
onClick={() => {
const cacheProps = animate;
const data: IStoreData = store.getData();
data.block.forEach((v) => {
if (v.id === props.current.id) {
v.animate = [];
}
});
store.forceUpdate();
setTimeout(() => {
if (!isOmit) {
isOmit = true;
const cacheProps = animate;
const data: IStoreData = store.getData();
data.block.forEach((v) => {
if (v.id === props.current.id) {
v.animate = cacheProps;
v.animate = [];
}
});
store.emit();
store.forceUpdate();
});
setTimeout(() => {
data.block.forEach((v) => {
if (v.id === props.current.id) {
v.animate = cacheProps;
}
});
store.emit();
store.forceUpdate();
isOmit = false;
});
}
}}
>

View File

@@ -142,7 +142,7 @@ Contributions, issues and feature requests are welcome!.
### 技术交流 | Technical Communication
<img src="http://cdn.dooring.cn/dr/WechatIMG679.jpeg" width=260 />
<a href="http://cdn.dooring.cn/dr/gp.jpeg" target="_blank"><img src="http://cdn.dooring.cn/dr/gp.jpeg" width=260 />dooringx-lib交流群</a>
## License

View File

@@ -1,5 +1,5 @@
{
"version": "0.8.0",
"version": "0.8.1",
"license": "MIT",
"main": "dist/index.js",
"module": "dist/dooringx-lib.esm.js",

View File

@@ -2,7 +2,7 @@
* @Author: yehuozhili
* @Date: 2021-03-14 04:29:09
* @LastEditors: yehuozhili
* @LastEditTime: 2021-07-20 11:24:39
* @LastEditTime: 2021-08-11 16:39:24
* @FilePath: \dooringx\packages\dooringx-lib\src\components\IframeWrapperMove\index.tsx
*/
import { AllHTMLAttributes, CSSProperties, PropsWithChildren, ReactNode, useRef } from 'react';
@@ -11,6 +11,7 @@ import { onWheelEventIframe } from '../../core/scale';
import React from 'react';
import Ticker from './ticker';
import UserConfig from '../../config';
import TimeLine from '../timeLine/timeline';
export interface ContainerWrapperProps extends AllHTMLAttributes<HTMLDivElement> {
config: UserConfig;
@@ -48,6 +49,7 @@ function ContainerWrapper(props: PropsWithChildren<ContainerWrapperProps>) {
{...onWheelEventIframe(props.config, scaleState)}
{...rest}
>
{config.timeline && <TimeLine config={config}></TimeLine>}
<div
style={{
position: 'absolute',

View File

@@ -128,7 +128,6 @@ function Blocks(props: PropsWithChildren<BlockProps>) {
});
return select;
}, [props.data.animate]);
console.log(animateProps);
const render = useMemo(() => {
// 如果是编辑模式下,则需要包裹不能选中层,位移层,缩放控制层,平面移动层。

View File

@@ -8,6 +8,7 @@ import {
MenuOutlined,
SyncOutlined,
UnorderedListOutlined,
VideoCameraOutlined,
} from '@ant-design/icons';
import { Button, Divider, Form, Input, List, Modal, Popconfirm, Popover } from 'antd';
import React, { CSSProperties, PropsWithChildren, useState } from 'react';
@@ -189,6 +190,13 @@ export function Control(props: PropsWithChildren<ControlProps>) {
<Button icon={<GatewayOutlined />}></Button>
</Popover>
<Button
icon={<VideoCameraOutlined />}
onClick={() => {
props.config.timeline = !props.config.timeline;
props.config.getStore().forceUpdate();
}}
></Button>
<Button
icon={<SyncOutlined />}
onClick={() => {

View File

@@ -0,0 +1,307 @@
/*
* @Author: yehuozhili
* @Date: 2021-08-09 15:15:25
* @LastEditors: yehuozhili
* @LastEditTime: 2021-08-11 17:03:54
* @FilePath: \dooringx\packages\dooringx-lib\src\components\timeLine\timeline.tsx
*/
import deepcopy from 'deepcopy';
import React, { CSSProperties, useState } from 'react';
import { SortableContainer, SortableElement, SortableHandle, SortEnd } from 'react-sortable-hoc';
import UserConfig from '../../config';
import { IBlockType, IStoreData } from '../../core/store/storetype';
import { arrayMove } from '../../core/utils';
import { MenuOutlined, PlayCircleOutlined } from '@ant-design/icons';
import {
TimeLineItem,
itemHeight,
TimeLineItemMouseMove,
TimeLineItemMouseOver,
interval,
} from './timelineItem';
export interface TimeLineProps {
style?: CSSProperties;
classes?: string;
config: UserConfig;
}
const iter = 500;
const animateTicker = new Array(iter).fill(1).map((_, y) => y);
const DragHandle = SortableHandle(() => <MenuOutlined />);
const leftWidth = 200;
let WAIT = false;
const widthInterval = interval * 10 + 9;
const ruleWidth = (widthInterval * iter) / 10 + 10;
const borderColor = '1px solid rgb(204, 204, 204)';
const SortableItem = SortableElement(
({ value }: { value: { value: IBlockType; config: UserConfig } }) => (
<div
style={{
userSelect: 'none',
display: 'flex',
alignItems: 'center',
width: '100%',
zIndex: 101,
}}
>
<div
style={{
display: 'flex',
alignItems: 'center',
width: leftWidth,
overflow: 'auto',
minWidth: leftWidth,
borderRight: borderColor,
borderBottom: borderColor,
}}
>
<div style={{ width: 30, cursor: 'move' }}>
<DragHandle></DragHandle>
</div>
<div>{value.config.getComponentRegister().getMap()[value.value.name].display}</div>
<div>{value.value.id.slice(-6)}</div>
</div>
</div>
)
);
const SortableList = SortableContainer(
({ items }: { items: { data: IBlockType[]; config: UserConfig } }) => {
return (
<div>
{items.data.map((value, index: number) => (
<SortableItem key={value.id} index={index} value={{ value, config: items.config }} />
))}
</div>
);
}
);
export function TimeLine(props: TimeLineProps) {
const store = props.config.getStore();
const data = store.getData().block;
const forceUpdate = useState(0)[1];
const onSortEnd = (sort: SortEnd) => {
const { oldIndex, newIndex } = sort;
const newblocks: IBlockType[] = arrayMove(data, oldIndex, newIndex);
const isEdit = props.config.getStoreChanger().isEdit();
if (isEdit) {
const firstType = newblocks[0].name;
if (firstType !== 'modalMask') {
return;
}
}
const store = props.config.getStore();
const cloneData: IStoreData = deepcopy(store.getData());
cloneData.block = newblocks;
store.setData(cloneData);
};
const [state, setState] = useState(0);
const [scrollx, setScrollx] = useState(0);
const content = (
<div style={{ width: '100%', height: '100%', overflow: 'hidden' }}>
<div style={{ transform: `translate(0, -${state}px)` }}>
<SortableList
distance={2}
useDragHandle
items={{
data,
config: props.config,
}}
onSortEnd={onSortEnd}
axis="y"
></SortableList>
</div>
</div>
);
return (
<div
className={props.classes}
style={{
backgroundColor: 'white',
width: '100%',
position: 'absolute',
height: '150px',
bottom: '0',
zIndex: 100,
display: 'flex',
flexDirection: 'column',
...props.style,
}}
>
<>
<div
style={{
display: 'flex',
lineHeight: '24px',
paddingLeft: 10,
height: '100%',
}}
>
<div>
<div
style={{
width: leftWidth,
borderRight: '1px solid #dadada',
minWidth: leftWidth,
borderBottom: '1px solid #dadada',
height: itemHeight,
}}
>
<span
title="play"
style={{
display: 'inline-block',
marginLeft: '20px',
cursor: 'pointer',
}}
onClick={() => {
//缓存所有animate后执行
if (!WAIT) {
WAIT = true;
const cache = data.map((v) => {
return v.animate;
});
const cloneData: IStoreData = deepcopy(store.getData());
cloneData.block.forEach((v) => {
v.animate = [];
});
store.setData(cloneData);
setTimeout(() => {
const cloneData: IStoreData = deepcopy(store.getData());
cloneData.block.forEach((v, i) => {
v.animate = cache[i];
});
WAIT = false;
store.setData(cloneData);
});
}
}}
>
<PlayCircleOutlined />
</span>
</div>
{content}
</div>
<div
style={{
width: `calc(100% - ${leftWidth}px)`,
borderBottom: '1px solid #dadada',
overflow: 'hidden',
}}
>
<div
style={{
display: 'flex',
height: itemHeight,
alignItems: 'flex-end',
borderBottom: borderColor,
width: ruleWidth,
overflow: 'hidden',
transform: `translate(-${scrollx}px, 0px)`,
}}
>
{animateTicker.map((v) => {
if (v % 10 === 0) {
return (
<div
key={v}
style={{
marginLeft: interval,
height: '8px',
borderLeft: borderColor,
position: 'relative',
}}
>
<div
style={{
position: 'absolute',
top: '-20px',
transform: 'translate(-50%, 0px)',
}}
>
{v}
</div>
</div>
);
} else {
return (
<div
key={v}
style={{
marginLeft: interval,
height: '6px',
borderLeft: borderColor,
}}
></div>
);
}
})}
</div>
<div
onScroll={(e) => {
const target = e.target as HTMLDivElement;
setState(target.scrollTop);
setScrollx(target.scrollLeft);
}}
style={{ overflow: 'auto', height: `calc(100% - ${itemHeight}px)` }}
>
{data.map((v) => {
return (
<div
key={v.id}
style={{
display: 'flex',
alignItems: 'center',
paddingLeft: interval,
borderBottom: borderColor,
width: ruleWidth,
position: 'relative',
overflow: 'hidden',
}}
onMouseMove={(e) => {
TimeLineItemMouseMove(e, v.animate, forceUpdate);
}}
onMouseLeave={() => TimeLineItemMouseOver()}
onMouseUp={() => TimeLineItemMouseOver()}
>
<TimeLineItem animate={v.animate}></TimeLineItem>
{animateTicker.map((v) => {
if (v % 10 === 0) {
return (
<div
key={v}
style={{
marginRight: widthInterval, // 左右2根线
borderLeft: borderColor,
position: 'relative',
height: itemHeight - 1,
}}
></div>
);
} else {
return null;
}
})}
</div>
);
})}
</div>
</div>
</div>
</>
</div>
);
}
export default TimeLine;

View File

@@ -0,0 +1,108 @@
/*
* @Author: yehuozhili
* @Date: 2021-08-10 20:26:44
* @LastEditors: yehuozhili
* @LastEditTime: 2021-08-11 15:34:46
* @FilePath: \dooringx\packages\dooringx-lib\src\components\timeLine\timelineItem.tsx
*/
import React from 'react';
import { AnimateItem } from '../../core/store/storetype';
export const itemHeight = 25;
const diff = 6;
// 需要根据animate属性渲染div
export interface TimeLineItemProps {
animate: AnimateItem[];
}
const bgColor = [
'#4af',
'rgb(93, 128, 158)',
'rgb(158, 130, 93)',
'rgb(219, 72, 34)',
'rgb(255, 68, 168)',
'#4af',
'rgb(93, 128, 158)',
'rgb(158, 130, 93)',
'rgb(219, 72, 34)',
'rgb(255, 68, 168)',
];
interface MoveStateTypes {
startX: number;
isMove: boolean;
uid: string;
dom: null | HTMLDivElement;
}
const moveState: MoveStateTypes = {
startX: 0,
isMove: false,
uid: '',
dom: null,
};
export const interval = 19;
const times = interval + 1;
export const TimeLineItemMouseMove = function (
e: React.MouseEvent<HTMLDivElement, MouseEvent>,
animate: AnimateItem[],
forceUpdate: React.Dispatch<React.SetStateAction<number>>
) {
if (moveState.isMove) {
//修改源属性
const diff = e.screenX - moveState.startX;
animate.forEach((v) => {
if (v.uid === moveState.uid) {
const f = parseFloat((v.animationDelay + diff / times).toFixed(1));
v.animationDelay = f < 0 ? 0 : f;
forceUpdate((p) => p + 1);
}
});
moveState.startX = e.screenX;
}
};
export const TimeLineItemMouseOver = function () {
moveState.isMove = false;
moveState.startX = 0;
moveState.uid = '';
if (moveState.dom) {
moveState.dom.style.cursor = 'default';
}
};
export function TimeLineItem(props: TimeLineItemProps) {
return (
<>
{props.animate.map((v) => {
const left = v.animationDelay * times + interval;
const repeat =
v.animationIterationCount === 'infinite' ? 500 : parseInt(v.animationIterationCount);
const width = v.animationDuration * times * repeat;
const index = v.uid.charCodeAt(0) % 10;
return (
<div
key={v.uid}
onMouseDown={(e) => {
moveState.startX = e.screenX;
moveState.uid = v.uid;
moveState.isMove = true;
const dom = e.target as HTMLDivElement;
dom.style.cursor = 'move';
moveState.dom = dom;
}}
style={{
position: 'absolute',
top: diff / 2,
left: left,
width: width,
height: itemHeight - diff,
background: bgColor[index],
}}
></div>
);
})}
</>
);
}

View File

@@ -2,7 +2,7 @@
* @Author: yehuozhili
* @Date: 2021-03-14 04:29:09
* @LastEditors: yehuozhili
* @LastEditTime: 2021-07-27 10:17:01
* @LastEditTime: 2021-08-11 16:16:30
* @FilePath: \dooringx\packages\dooringx-lib\src\components\wrapperMove\index.tsx
*/
import { AllHTMLAttributes, CSSProperties, PropsWithChildren, useRef } from 'react';
@@ -11,6 +11,7 @@ import { onWheelEvent } from '../../core/scale';
import React from 'react';
import Ticker from './ticker';
import UserConfig from '../../config';
import TimeLine from '../timeLine/timeline';
export interface ContainerWrapperProps extends AllHTMLAttributes<HTMLDivElement> {
config: UserConfig;
@@ -42,6 +43,7 @@ function ContainerWrapper(props: PropsWithChildren<ContainerWrapperProps>) {
{...onWheelEvent(props.config)}
{...rest}
>
{config.timeline && <TimeLine config={config}></TimeLine>}
{children}
{ticker && <Ticker config={props.config}></Ticker>}
</div>

View File

@@ -2,7 +2,7 @@
* @Author: yehuozhili
* @Date: 2021-02-25 21:16:58
* @LastEditors: yehuozhili
* @LastEditTime: 2021-08-03 23:19:24
* @LastEditTime: 2021-08-11 16:16:09
* @FilePath: \dooringx\packages\dooringx-lib\src\config\index.tsx
*/
import React from 'react';
@@ -344,6 +344,7 @@ export class UserConfig {
public focusState = focusState;
public collapsed = false;
public ticker = true;
public timeline = false;
public wrapperMoveState = wrapperMoveState;
public iframeWrapperMoveState = iframeWrapperMoveState;
public refreshIframe = () => {};