Merge pull request #30 from H5-Dooring/animate-patch

Animate patch
This commit is contained in:
yehuozhili
2022-04-01 14:36:41 +08:00
committed by GitHub
10 changed files with 653 additions and 156 deletions

View File

@@ -1,9 +1,22 @@
import React, { useMemo } from 'react'; import React, { useMemo, useState } from 'react';
import { UserConfig, deepCopy, createUid } from 'dooringx-lib'; import { UserConfig, deepCopy, createUid } from 'dooringx-lib';
import { Col, Row, Select, InputNumber, Button } from 'antd'; import {
Col,
Row,
Select,
InputNumber,
Button,
Modal,
Form,
Space,
Input,
Table,
Popconfirm,
} from 'antd';
import { FormMap, FormBaseType } from '../formTypes'; import { FormMap, FormBaseType } from '../formTypes';
import { CreateOptionsRes } from 'dooringx-lib/dist/core/components/formTypes'; import { CreateOptionsRes } from 'dooringx-lib/dist/core/components/formTypes';
import { AnimateItem, IBlockType, IStoreData } from 'dooringx-lib/dist/core/store/storetype'; import { AnimateItem, IBlockType, IStoreData } from 'dooringx-lib/dist/core/store/storetype';
import { PlusOutlined } from '@ant-design/icons';
export interface FormAnimateControlType extends FormBaseType {} export interface FormAnimateControlType extends FormBaseType {}
@@ -13,7 +26,7 @@ interface AnimateControlProps {
config: UserConfig; config: UserConfig;
} }
//类型待修改 // 左侧实际类名-显示名称
const animateCategory: Record<string, string> = { const animateCategory: Record<string, string> = {
'': '无', '': '无',
animate__bounce: 'bounce', animate__bounce: 'bounce',
@@ -134,167 +147,240 @@ function AnimateControl(props: AnimateControlProps) {
lastAnimate = props.current.animate; lastAnimate = props.current.animate;
return props.current.animate; return props.current.animate;
}, [props.current.animate]); }, [props.current.animate]);
const [customModal, setCustomModal] = useState(false);
const [form] = Form.useForm();
const [deletModal, setDeletModal] = useState(false);
const customAnimate = props.config.animateFactory.getCustomAnimateName();
const columns = [
{
title: '动画名称',
dataIndex: 'animateName',
width: 150,
},
{
title: '显示名称',
dataIndex: 'displayName',
width: 150,
},
{
title: '动画详情',
dataIndex: 'keyframe',
},
{
title: '操作',
width: 120,
render: (_: any, record: any) => (
<Space size="middle">
<Popconfirm
onConfirm={() => {
const name = record.animateName;
props.config.animateFactory.deleteCustomAnimate(name);
props.config.animateFactory.deleteKeyFrameAnimate(name);
props.config.animateFactory.syncToStore(props.config);
}}
title="确定删除吗?"
>
<a></a>
</Popconfirm>
</Space>
),
},
];
return ( return (
<> <>
<Space
style={{ display: 'flex', marginBottom: 8, justifyContent: 'space-around' }}
align="baseline"
>
<Button
onClick={() => {
setCustomModal(true);
}}
>
</Button>
<Button
onClick={() => {
setDeletModal(true);
}}
>
</Button>
</Space>
{animate.map((v, i) => { {animate.map((v, i) => {
return ( return (
<div key={v.uid} style={{ borderBottom: '1px dotted #9e9e9e' }}> <div key={v.uid} style={{ borderBottom: '1px dotted #9e9e9e' }}>
<Row style={{ padding: padding, alignItems: 'center' }}> {
<Col span={5}>:</Col> <>
<Col span={7}> <Row style={{ padding: padding, alignItems: 'center' }}>
<Select <Col span={5}>:</Col>
value={animate[i].animationName} <Col span={7}>
style={{ width: '100%' }} <Select
onChange={(d) => { value={animate[i].animationName}
const cloneData: IStoreData = deepCopy(store.getData()); style={{ width: '100%' }}
cloneData.block.forEach((w) => { onChange={(d) => {
if (w.id === props.current.id) { const cloneData: IStoreData = deepCopy(store.getData());
w.animate.forEach((f) => { cloneData.block.forEach((w) => {
if (f.uid === v.uid) { if (w.id === props.current.id) {
f.animationName = d; w.animate.forEach((f) => {
if (f.uid === v.uid) {
f.animationName = d;
}
});
} }
}); });
} store.setData(cloneData);
}); }}
store.setData(cloneData); >
}} {Object.keys(animateCategory).map((v, i) => {
> return (
{Object.keys(animateCategory).map((v, i) => { <Select.Option key={i} value={animateCategory[v]}>
return ( {animateCategory[v]}
<Select.Option key={i} value={animateCategory[v]}> </Select.Option>
{animateCategory[v]} );
</Select.Option> })}
); {customAnimate.map((v) => {
})} return (
</Select> <Select.Option key={v.animateName} value={v.animateName}>
</Col> {v.displayName}
<Col span={5} style={{ paddingLeft: '10px' }}> </Select.Option>
: );
</Col> })}
<Col span={7}> </Select>
<InputNumber </Col>
style={{ width: '100%' }} <Col span={5} style={{ paddingLeft: '10px' }}>
step="0.1" :
value={animate[i].animationDuration} </Col>
formatter={(value) => `${value}s`} <Col span={7}>
min={0} <InputNumber
onChange={(d) => { style={{ width: '100%' }}
const cloneData: IStoreData = deepCopy(store.getData()); step="0.1"
cloneData.block.forEach((w) => { value={animate[i].animationDuration}
if (w.id === props.current.id) { formatter={(value) => `${value}s`}
w.animate.forEach((f) => { min={0}
if (f.uid === v.uid) { onChange={(d) => {
f.animationDuration = d; const cloneData: IStoreData = deepCopy(store.getData());
cloneData.block.forEach((w) => {
if (w.id === props.current.id) {
w.animate.forEach((f) => {
if (f.uid === v.uid) {
f.animationDuration = d;
}
});
} }
}); });
} store.setData(cloneData);
}); }}
store.setData(cloneData); />
}} </Col>
/> </Row>
</Col> <Row style={{ padding: padding, alignItems: 'center' }}>
</Row> <Col span={5}>:</Col>
<Row style={{ padding: padding, alignItems: 'center' }}> <Col span={7}>
<Col span={5}>:</Col> <InputNumber
<Col span={7}> style={{ width: '100%' }}
<InputNumber value={animate[i].animationDelay}
style={{ width: '100%' }} formatter={(value) => `${value}s`}
value={animate[i].animationDelay} min={0}
formatter={(value) => `${value}s`} step="0.1"
min={0} onChange={(d) => {
step="0.1" const cloneData: IStoreData = deepCopy(store.getData());
onChange={(d) => { cloneData.block.forEach((w) => {
const cloneData: IStoreData = deepCopy(store.getData()); if (w.id === props.current.id) {
cloneData.block.forEach((w) => { w.animate.forEach((f) => {
if (w.id === props.current.id) { if (f.uid === v.uid) {
w.animate.forEach((f) => { f.animationDelay = d;
if (f.uid === v.uid) { }
f.animationDelay = d; });
} }
}); });
} store.setData(cloneData);
}); }}
store.setData(cloneData); />
}} </Col>
/> <Col span={5} style={{ paddingLeft: '10px' }}>
</Col> :
<Col span={5} style={{ paddingLeft: '10px' }}> </Col>
: <Col span={7}>
</Col> <Select
<Col span={7}> value={animate[i].animationIterationCount}
<Select style={{ width: '100%' }}
value={animate[i].animationIterationCount} onChange={(d) => {
style={{ width: '100%' }} const cloneData: IStoreData = deepCopy(store.getData());
onChange={(d) => { cloneData.block.forEach((w) => {
const cloneData: IStoreData = deepCopy(store.getData()); if (w.id === props.current.id) {
cloneData.block.forEach((w) => { w.animate.forEach((f) => {
if (w.id === props.current.id) { if (f.uid === v.uid) {
w.animate.forEach((f) => { f.animationIterationCount = d;
if (f.uid === v.uid) { }
f.animationIterationCount = d; });
} }
}); });
} store.setData(cloneData);
}); }}
store.setData(cloneData); >
}} {repeat.map((v, i) => {
> return (
{repeat.map((v, i) => { <Select.Option key={i} value={v}>
return ( {v}
<Select.Option key={i} value={v}> </Select.Option>
{v} );
</Select.Option> })}
); </Select>
})} </Col>
</Select> </Row>
</Col> <Row style={{ padding: padding, alignItems: 'center' }}>
</Row> <Col span={5}>:</Col>
<Row style={{ padding: padding, alignItems: 'center' }}> <Col span={7}>
<Col span={5}>:</Col> <Select
<Col span={7}> value={animate[i].animationTimingFunction}
<Select style={{ width: '100%' }}
value={animate[i].animationTimingFunction} onChange={(d) => {
style={{ width: '100%' }} const cloneData: IStoreData = deepCopy(store.getData());
onChange={(d) => { cloneData.block.forEach((w) => {
const cloneData: IStoreData = deepCopy(store.getData()); if (w.id === props.current.id) {
cloneData.block.forEach((w) => { w.animate.forEach((f) => {
if (w.id === props.current.id) { if (f.uid === v.uid) {
w.animate.forEach((f) => { f.animationTimingFunction = d;
if (f.uid === v.uid) { }
f.animationTimingFunction = d; });
} }
}); });
} store.setData(cloneData);
}); }}
store.setData(cloneData); >
}} {Object.keys(timeFunction).map((v, i) => {
> return (
{Object.keys(timeFunction).map((v, i) => { <Select.Option key={i} value={timeFunction[v]}>
return ( {v}
<Select.Option key={i} value={timeFunction[v]}> </Select.Option>
{v} );
</Select.Option> })}
); </Select>
})} </Col>
</Select> <Col style={{ justifyContent: 'flex-end', flex: 1, textAlign: 'end' }}>
</Col> <Button
<Col style={{ justifyContent: 'flex-end', flex: 1, textAlign: 'end' }}> danger
<Button onClick={() => {
danger const cloneData: IStoreData = deepCopy(store.getData());
onClick={() => { cloneData.block.map((v) => {
const cloneData: IStoreData = deepCopy(store.getData()); if (v.id === props.current.id) {
cloneData.block.map((v) => { v.animate.splice(i, 1);
if (v.id === props.current.id) { }
v.animate.splice(i, 1); });
} store.setData(cloneData);
}); }}
store.setData(cloneData); >
}}
> </Button>
</Col>
</Button> </Row>
</Col> </>
</Row> }
</div> </div>
); );
})} })}
@@ -357,6 +443,150 @@ function AnimateControl(props: AnimateControlProps) {
</Button> </Button>
</Row> </Row>
<Modal
width={800}
title={'设置自定义动画'}
forceRender
visible={customModal}
onOk={() => {
form.validateFields().then((res) => {
const values = { ...res };
props.config.animateFactory.addUserInputIntoCustom(values, props.config);
setCustomModal(false);
form.resetFields();
});
}}
onCancel={() => {
setCustomModal(false);
form.resetFields();
}}
>
<Form labelCol={{ span: 11 }} form={form}>
<Form.Item
required
rules={[{ required: true, message: '请输入名称!' }]}
labelCol={{ span: 6 }}
name="displayName"
label="自定义动画显示名称"
>
<Input></Input>
</Form.Item>
<Form.Item
initialValue={`dooringx_${Math.random().toString(36).slice(2)}`}
labelCol={{ span: 6 }}
name="animateName"
label="自定义动画名称"
>
<Input disabled></Input>
</Form.Item>
<Form.List name="keyframes">
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }) => (
<Space
key={key}
style={{ display: 'flex', marginBottom: 8, flexWrap: 'wrap' }}
align="baseline"
>
<Form.Item
style={{ width: 180 }}
label="时间百分比"
{...restField}
name={[name, 'percent']}
initialValue={0}
>
<InputNumber min={0} max={100} formatter={(value) => `${value}%`} />
</Form.Item>
<>
<Form.Item
style={{ width: 180 }}
label="坐标偏移X"
{...restField}
name={[name, 'positionX']}
initialValue={0}
>
<InputNumber />
</Form.Item>
<Form.Item
style={{ width: 180 }}
label="坐标偏移Y"
{...restField}
name={[name, 'positionY']}
initialValue={0}
>
<InputNumber />
</Form.Item>
</>
<Form.Item
style={{ width: 180 }}
label="旋转"
{...restField}
name={[name, 'rotate']}
initialValue={0}
>
<InputNumber formatter={(value) => `${value}°`} />
</Form.Item>
<Form.Item
style={{ width: 180 }}
label="缩放"
{...restField}
name={[name, 'scale']}
initialValue={100}
>
<InputNumber formatter={(value) => `${value}%`} />
</Form.Item>
<Form.Item
style={{ width: 180 }}
label="透明度"
{...restField}
name={[name, 'opacity']}
initialValue={100}
>
<InputNumber formatter={(value) => `${value}%`} />
</Form.Item>
<Button
danger
onClick={() => {
remove(name);
}}
>
</Button>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
icon={<PlusOutlined />}
>
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form>
</Modal>
<Modal
width={800}
title={'删除自定义动画'}
forceRender
visible={deletModal}
footer={null}
onOk={() => {
setDeletModal(false);
}}
onCancel={() => {
setDeletModal(false);
}}
>
<Table columns={columns} dataSource={customAnimate}></Table>
</Modal>
</> </>
); );
} }

View File

@@ -29,6 +29,13 @@ const LeftRegistMap: LeftRegistComponentMapItem[] = [
img: 'https://img.guguzhu.com/d/file/android/ico/2021/09/08/rytzi2w34tm.png', img: 'https://img.guguzhu.com/d/file/android/ico/2021/09/08/rytzi2w34tm.png',
displayName: '输入框', displayName: '输入框',
}, },
{
type: 'basic',
component: 'test',
img: 'https://img.guguzhu.com/d/file/android/ico/2021/09/08/rytzi2w34tm.png',
displayName: '测试',
urlFn: () => import('./registComponents/testCo'),
},
]; ];
export const defaultConfig: Partial<InitConfig> = { export const defaultConfig: Partial<InitConfig> = {

View File

@@ -0,0 +1,17 @@
import { ComponentItemFactory } from 'dooringx-lib';
const TestCo = new ComponentItemFactory(
'test',
'测试组件',
{},
{
width: 200,
height: 55,
},
() => {
return <div></div>;
},
true
);
export default TestCo;

View File

@@ -2,7 +2,7 @@
* @Author: yehuozhili * @Author: yehuozhili
* @Date: 2021-03-14 05:40:37 * @Date: 2021-03-14 05:40:37
* @LastEditors: yehuozhili * @LastEditors: yehuozhili
* @LastEditTime: 2021-10-10 00:54:55 * @LastEditTime: 2022-04-01 14:29:39
* @FilePath: \dooringx\packages\dooringx-lib\src\components\preview.tsx * @FilePath: \dooringx\packages\dooringx-lib\src\components\preview.tsx
*/ */
import Container from './container'; import Container from './container';
@@ -62,10 +62,17 @@ function Preview(props: PreviewProps): ReactElement {
.syncEventMap(props.config.getStore().getData(), props.config.getStoreChanger()); .syncEventMap(props.config.getStore().getData(), props.config.getStoreChanger());
// 设置全局 // 设置全局
const bodyColor = props.config.getStore().getData().globalState?.bodyColor; const global = props.config.getStore().getData().globalState;
const bodyColor = global?.bodyColor;
if (bodyColor) { if (bodyColor) {
document.body.style.backgroundColor = bodyColor; document.body.style.backgroundColor = bodyColor;
} }
const customAnimate = global?.customAnimate;
if (customAnimate && Array.isArray(customAnimate)) {
// 插入自定义动画
props.config.animateFactory.fromArrInsertKeyFrame(customAnimate);
}
if (props.completeFn) { if (props.completeFn) {
props.completeFn(); props.completeFn();
} }

View File

@@ -84,7 +84,11 @@ function RightConfig(props: PropsWithChildren<RightConfigProps>) {
); );
}); });
} else { } else {
return <div>{replaceLocale('right.noprops', '还没有配置属性', props.config)}</div>; return (
<div className="yh-right-noprops" style={{ textAlign: 'center' }}>
{replaceLocale('right.noprops', '还没有配置属性', props.config)}
</div>
);
} }
} }
return null; return null;

View File

@@ -2,7 +2,7 @@
* @Author: yehuozhili * @Author: yehuozhili
* @Date: 2021-08-09 15:15:25 * @Date: 2021-08-09 15:15:25
* @LastEditors: yehuozhili * @LastEditors: yehuozhili
* @LastEditTime: 2022-01-12 17:44:22 * @LastEditTime: 2022-04-01 13:41:34
* @FilePath: \dooringx\packages\dooringx-lib\src\components\timeLine\timeline.tsx * @FilePath: \dooringx\packages\dooringx-lib\src\components\timeLine\timeline.tsx
*/ */
import deepcopy from 'deepcopy'; import deepcopy from 'deepcopy';
@@ -457,7 +457,13 @@ export function TimeLine(props: TimeLineProps) {
}} }}
onMouseDown={(e) => { onMouseDown={(e) => {
const dom = e.target as HTMLDivElement; const dom = e.target as HTMLDivElement;
if (!(dom.className && dom.className.indexOf('yh-timeline-item-mainblock') > -1)) { if (
!(
dom.className &&
dom.className.indexOf &&
dom.className.indexOf('yh-timeline-item-mainblock') > -1
)
) {
resetCurrentMoveItemId(); resetCurrentMoveItemId();
} }
}} }}

View File

@@ -6,7 +6,7 @@
* @FilePath: \dooringx\packages\dooringx-lib\src\config\index.tsx * @FilePath: \dooringx\packages\dooringx-lib\src\config\index.tsx
*/ */
import React from 'react'; import React from 'react';
import { IBlockType, IStoreData } from '../core/store/storetype'; import { IBlockType, IMainStoreData, IStoreData } from '../core/store/storetype';
import { ComponentClass, FunctionComponent, ReactNode } from 'react'; import { ComponentClass, FunctionComponent, ReactNode } from 'react';
import { ComponentItemFactory } from '../core/components/abstract'; import { ComponentItemFactory } from '../core/components/abstract';
import { marklineConfig } from '../core/markline/marklineConfig'; import { marklineConfig } from '../core/markline/marklineConfig';
@@ -34,6 +34,7 @@ import { VerticalAlignMiddleOutlined } from '@ant-design/icons';
import { wrapperMoveState } from '../components/wrapperMove/event'; import { wrapperMoveState } from '../components/wrapperMove/event';
import { wrapperMoveState as iframeWrapperMoveState } from '../components/IframeWrapperMove/event'; import { wrapperMoveState as iframeWrapperMoveState } from '../components/IframeWrapperMove/event';
import { TimeLineConfigType, TimeLineNeedleConfigType } from '../components/timeLine/timeline'; import { TimeLineConfigType, TimeLineNeedleConfigType } from '../components/timeLine/timeline';
import { AnimateFactory } from '../core/AnimateFactory';
// 组件部分 // 组件部分
/** /**
@@ -158,7 +159,7 @@ export interface InitConfig {
containerIcon: ReactNode; containerIcon: ReactNode;
} }
export const defaultStore: IStoreData = { export const defaultStore: IMainStoreData = {
container: { container: {
width: 375, width: 375,
height: 667, height: 667,
@@ -173,6 +174,7 @@ export const defaultStore: IStoreData = {
title: 'dooring', title: 'dooring',
bodyColor: 'rgba(255,255,255,1)', bodyColor: 'rgba(255,255,255,1)',
script: [], script: [],
customAnimate: [],
}, },
modalConfig: {}, modalConfig: {},
}; };
@@ -337,6 +339,7 @@ export class UserConfig {
public componentRegister = new ComponentRegister(); public componentRegister = new ComponentRegister();
public formRegister = new FormComponentRegister(); public formRegister = new FormComponentRegister();
public storeChanger = new StoreChanger(); public storeChanger = new StoreChanger();
public animateFactory = new AnimateFactory();
public componentCache = {}; public componentCache = {};
public asyncComponentUrlMap = {} as AsyncCacheComponentType; public asyncComponentUrlMap = {} as AsyncCacheComponentType;
public marklineConfig = marklineConfig; public marklineConfig = marklineConfig;

View File

@@ -0,0 +1,198 @@
import UserConfig from '../../config';
import { CustomAnimateObj, IMainStoreData, IStoreData } from '../store/storetype';
import { deepCopy } from '../utils';
/**
*
* opacity: 100
percent: 0
positionX: 0
positionY: 0
rotate: 0
scale: 100
* @export 转换使用
* @interface TransformItemObj
*/
export interface TransformItemObj {
opacity: number;
percent: number;
positionX: number;
positionY: number;
rotate: number;
scale: number;
}
/**
*
*
* @export 用户输入对象
* @interface TransformItem
*/
export interface TransformItem {
displayName: string;
animateName: string;
keyframes: TransformItemObj[];
}
export class AnimateFactory {
constructor(public customAnimateName: Array<CustomAnimateObj> = []) {}
getCustomAnimateName() {
return this.customAnimateName;
}
getStyleSheets() {
return document.styleSheets;
}
/**
*
* 插入动画
* @param {string} ruleText
* @memberof AnimateFactory
*/
inserKeyframeAnimate(ruleText: string) {
const sheets = this.getStyleSheets();
if (sheets.length === 0) {
let style = document.createElement('style');
style.appendChild(document.createTextNode(''));
document.head.appendChild(style);
}
// const len = sheets.length;
// let ss: number | null = null;
// let st: number | null = null;
// for (let i = 0; i < len; i++) {
// for (let k = 0; k < sheets[i].cssRules.length; k++) {
// const rule = sheets[i].cssRules[k] as CSSKeyframesRule;
// const name = rule?.name;
// if (name && name === keyframeName) {
// // 删除该keyframe
// ss = i;
// st = k;
// }
// }
// }
// if (ss !== null && st !== null) {
// sheets[ss].deleteRule(st);
// }
// let sheet = sheets[ss ? ss : sheets.length - 1] as CSSStyleSheet;
let sheet = sheets[0] as CSSStyleSheet; // 末尾的经常存在重复覆盖的问题
sheet.insertRule(ruleText, sheet.cssRules.length);
}
/**
*
* 删除keyframe
* @param {string} animateName
* @returns
* @memberof AnimateFactory
*/
deleteKeyFrameAnimate(animateName: string) {
const sheets = this.getStyleSheets();
if (sheets.length === 0) {
return;
}
const sheet = sheets[0] as CSSStyleSheet;
const len = sheet.cssRules.length;
let ss = null;
for (let i = 0; i < len; i++) {
const rule = sheet.cssRules[i] as CSSKeyframesRule;
const name = rule?.name;
if (name && name === animateName) {
ss = i;
}
}
if (ss !== null) {
sheet.deleteRule(ss);
}
}
/**
*
* 配置时使用
* @param {Array<CustomAnimateObj>} [customAnimateNameArr=[]]
* @memberof AnimateFactory
*/
addCustomAnimate(customAnimateNameArr: Array<CustomAnimateObj> = []) {
this.customAnimateName = [...this.customAnimateName, ...customAnimateNameArr];
}
/**
*
* 删除使用animateName 防止displayName重名 用完需要同步store
* @param {string} animateName
* @memberof AnimateFactory
*/
deleteCustomAnimate(animateName: string) {
this.customAnimateName = this.customAnimateName.filter((v) => v.animateName !== animateName);
}
/**
*
* 从配置项插入动画 导入设置
* @memberof AnimateFactory
*/
fromArrInsertKeyFrame(customAnimateName: Array<CustomAnimateObj> = this.customAnimateName) {
customAnimateName.forEach((v) => {
this.inserKeyframeAnimate(v.keyframe);
});
}
/**
*
* 将this.customAnimateName写入store
* @memberof AnimateFactory
*/
syncToStore(config: UserConfig) {
// 先判断global的位置
const store = config.getStore();
let data: IStoreData;
const isEdit = config.getStoreChanger().isEdit();
if (isEdit) {
const origin = config.getStoreChanger().getOrigin()!;
data = origin.data[origin.current];
} else {
data = store.getData();
}
const copy: IMainStoreData = deepCopy(data);
const originGlobal = copy.globalState as IMainStoreData['globalState'];
originGlobal.customAnimate = [...this.customAnimateName];
if (isEdit) {
config.getStoreChanger().updateOrigin(copy);
} else {
store.setData(copy);
}
}
/**
*
* 将用户输入转换为新的动画
* @param {TransformItem} item
* @memberof AnimateFactory
*/
addUserInputIntoCustom(item: TransformItem, config: UserConfig) {
// 先转换keyframe
const keyframeItem = item.keyframes.map((v) => {
return `${v.percent}% {
transform:translate(${v.positionX}px, ${v.positionY}px) scale(${(v.scale / 100).toFixed(
2
)}) rotate(${v.rotate}deg);
}`;
});
const keyframe = `@keyframes ${item.animateName} {
${keyframeItem.join(' ')}
}`;
const customAnimateNameArr: CustomAnimateObj[] = [
{
displayName: item.displayName,
keyframe,
animateName: item.animateName,
},
];
// 添加内置
this.addCustomAnimate(customAnimateNameArr);
// 插入动画
this.inserKeyframeAnimate(keyframe);
// 写入store
this.syncToStore(config);
}
}

View File

@@ -8,6 +8,15 @@
import { EventCenterMapType } from '../eventCenter'; import { EventCenterMapType } from '../eventCenter';
export interface GlobalState {
[key: string]: any;
customAnimate: CustomAnimateObj[];
containerColor: string;
title: string;
bodyColor: string;
script: string[];
}
export interface IStoreData { export interface IStoreData {
container: { container: {
width: number; width: number;
@@ -19,6 +28,10 @@ export interface IStoreData {
globalState: Record<string, any>; globalState: Record<string, any>;
modalConfig: Record<string, any>; modalConfig: Record<string, any>;
} }
export interface IMainStoreData extends IStoreData {
globalState: GlobalState;
}
export interface AnimateItem { export interface AnimateItem {
uid: string; uid: string;
animationName: string; animationName: string;
@@ -28,6 +41,12 @@ export interface AnimateItem {
animationTimingFunction: string; animationTimingFunction: string;
} }
export interface CustomAnimateObj {
displayName: string;
animateName: string;
keyframe: string;
}
export interface IBlockType { export interface IBlockType {
id: string; id: string;
name: string; name: string;

View File

@@ -82,6 +82,12 @@ export class StoreChanger {
return this.map[ORIGIN]; return this.map[ORIGIN];
} }
/**
* 判断是否在编辑模式。
* 一次也没进行编辑时storeChanger中未存store所以只能判断去获取。
* @return {*}
* @memberof StoreChanger
*/
isEdit() { isEdit() {
if (storeChangerState.modalEditName !== '') { if (storeChangerState.modalEditName !== '') {
return true; return true;