diff --git a/packages/dooringx-example/src/plugin/formComponents/animateControl.tsx b/packages/dooringx-example/src/plugin/formComponents/animateControl.tsx index 4828fd9..5912079 100644 --- a/packages/dooringx-example/src/plugin/formComponents/animateControl.tsx +++ b/packages/dooringx-example/src/plugin/formComponents/animateControl.tsx @@ -4,7 +4,7 @@ import { Col, Row, Select, InputNumber, Button, Modal, Form, Space, Input } from import { FormMap, FormBaseType } from '../formTypes'; import { CreateOptionsRes } from 'dooringx-lib/dist/core/components/formTypes'; import { AnimateItem, IBlockType, IStoreData } from 'dooringx-lib/dist/core/store/storetype'; -import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; +import { PlusOutlined } from '@ant-design/icons'; export interface FormAnimateControlType extends FormBaseType {} @@ -138,7 +138,7 @@ function AnimateControl(props: AnimateControlProps) { const [customModal, setCustomModal] = useState(false); const [form] = Form.useForm(); - + const [positionEnable, setPositionEnable] = useState([]); return ( <> { - console.log('ok', form.getFieldsValue()); + form.validateFields().then((res) => { + const values = { ...res }; + // 根据禁用情况去除xy + const result = values.keyframes.map((v: any, i: number) => { + if (!positionEnable[i]) { + // 做删除处理 + return { + ...v, + positionX: null, + positionY: null, + }; + } + return v; + }); + values.keyframes = result; + props.config.animateFactory.addUserInputIntoCustom(values, props.config); + setPositionEnable([]); + setCustomModal(false); + }); }} onCancel={() => { form.resetFields(); setCustomModal(false); }} > -
- + + - + {(fields, { add, remove }) => ( <> {fields.map(({ key, name, ...restField }) => ( - - - - - - - - - - - - - - - + + + `${value}%`} /> + {positionEnable[key] && ( + <> + + + + + + + + )} - remove(name)} /> + + `${value}°`} /> + + + `${value}%`} /> + + + `${value}%`} /> + + + ))} - diff --git a/packages/dooringx-lib/src/config/index.tsx b/packages/dooringx-lib/src/config/index.tsx index 40e7514..6a2ac5d 100644 --- a/packages/dooringx-lib/src/config/index.tsx +++ b/packages/dooringx-lib/src/config/index.tsx @@ -6,7 +6,7 @@ * @FilePath: \dooringx\packages\dooringx-lib\src\config\index.tsx */ 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 { ComponentItemFactory } from '../core/components/abstract'; import { marklineConfig } from '../core/markline/marklineConfig'; @@ -34,6 +34,7 @@ import { VerticalAlignMiddleOutlined } from '@ant-design/icons'; import { wrapperMoveState } from '../components/wrapperMove/event'; import { wrapperMoveState as iframeWrapperMoveState } from '../components/IframeWrapperMove/event'; import { TimeLineConfigType, TimeLineNeedleConfigType } from '../components/timeLine/timeline'; +import { AnimateFactory } from '../core/AnimateFactory'; // 组件部分 /** @@ -158,7 +159,7 @@ export interface InitConfig { containerIcon: ReactNode; } -export const defaultStore: IStoreData = { +export const defaultStore: IMainStoreData = { container: { width: 375, height: 667, @@ -173,6 +174,7 @@ export const defaultStore: IStoreData = { title: 'dooring', bodyColor: 'rgba(255,255,255,1)', script: [], + customAnimate: [], }, modalConfig: {}, }; @@ -337,6 +339,7 @@ export class UserConfig { public componentRegister = new ComponentRegister(); public formRegister = new FormComponentRegister(); public storeChanger = new StoreChanger(); + public animateFactory = new AnimateFactory(); public componentCache = {}; public asyncComponentUrlMap = {} as AsyncCacheComponentType; public marklineConfig = marklineConfig; diff --git a/packages/dooringx-lib/src/core/AnimateFactory/index.ts b/packages/dooringx-lib/src/core/AnimateFactory/index.ts new file mode 100644 index 0000000..8ae18dc --- /dev/null +++ b/packages/dooringx-lib/src/core/AnimateFactory/index.ts @@ -0,0 +1,178 @@ +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 | null; + positionY: number | null; + rotate: number; + scale: number; +} + +/** + * + * + * @export 用户输入对象 + * @interface TransformItem + */ +export interface TransformItem { + displayName: string; + animateName: string; + keyframes: TransformItemObj[]; +} + +export class AnimateFactory { + constructor(public customAnimateName: Array = []) {} + + getCustomAnimateName() { + return this.customAnimateName; + } + getStyleSheets() { + return document.styleSheets; + } + + /** + * + * 插入动画 + * @param {string} ruleText + * @param {string} keyframeName 动画名称 + * @memberof AnimateFactory + */ + inserKeyframeAnimate(ruleText: string, keyframeName: 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; + sheet.insertRule(ruleText, sheet.rules ? sheet.rules.length : sheet.cssRules.length); + } + + /** + * + * 配置时使用 + * @param {Array} [customAnimateNameArr=[]] + * @memberof AnimateFactory + */ + addCustomAnimate(customAnimateNameArr: Array = []) { + 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 = this.customAnimateName) { + customAnimateName.forEach((v) => { + this.inserKeyframeAnimate(v.keyframe, v.animateName); + }); + } + + /** + * + * 将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) => { + if (v.positionX !== null && v.positionY !== null) { + // 带入xy 否则不计算xy + return `${v.percent}% { + transform:translate(${v.positionX}px, ${v.positionY}px) scale(${(v.scale / 100).toFixed( + 2 + )}) rotate(${v.rotate}deg); + }`; + } else { + return `${v.percent}% { + transform: 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, item.animateName); + // 写入store + this.syncToStore(config); + } +} diff --git a/packages/dooringx-lib/src/core/dynamicAnimate/index.ts b/packages/dooringx-lib/src/core/dynamicAnimate/index.ts deleted file mode 100644 index e992e97..0000000 --- a/packages/dooringx-lib/src/core/dynamicAnimate/index.ts +++ /dev/null @@ -1,82 +0,0 @@ -export interface CustomAnimateObj { - displayName: string; - animateName: string; - keyframe: string; -} - -export class DynamicAnimate { - constructor(public customAnimateName: Array = []) {} - - getCustomAnimateName() { - return this.customAnimateName; - } - getStyleSheets() { - return document.styleSheets; - } - - /** - * - * 插入动画 - * @param {string} ruleText - * @param {string} keyframeName - * @memberof DynamicAnimate - */ - inserKeyframeAnimate(ruleText: string, keyframeName: 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; - sheet.insertRule(ruleText, sheet.rules ? sheet.rules.length : sheet.cssRules.length); - } - - /** - * - * 配置时使用 - * @param {Array} [customAnimateName=[]] - * @memberof DynamicAnimate - */ - addCustomAnimate(customAnimateName: Array = []) { - this.customAnimateName = [...this.customAnimateName, ...customAnimateName]; - } - - /** - * - * 删除使用animateName 防止displayName重名 - * @param {string} animateName - * @memberof DynamicAnimate - */ - deleteCustomAnimate(animateName: string) { - this.customAnimateName = this.customAnimateName.filter((v) => v.animateName !== animateName); - } - - /** - * - * 从配置项插入动画 导入设置 - * @memberof DynamicAnimate - */ - fromArrInsertKeyFrame(customAnimateName: Array = this.customAnimateName) { - customAnimateName.forEach((v) => { - this.inserKeyframeAnimate(v.keyframe, v.animateName); - }); - } -} diff --git a/packages/dooringx-lib/src/core/store/storetype.ts b/packages/dooringx-lib/src/core/store/storetype.ts index eecd3d7..93190b1 100644 --- a/packages/dooringx-lib/src/core/store/storetype.ts +++ b/packages/dooringx-lib/src/core/store/storetype.ts @@ -8,6 +8,15 @@ import { EventCenterMapType } from '../eventCenter'; +export interface GlobalState { + [key: string]: any; + customAnimate: CustomAnimateObj[]; + containerColor: string; + title: string; + bodyColor: string; + script: string[]; +} + export interface IStoreData { container: { width: number; @@ -19,6 +28,10 @@ export interface IStoreData { globalState: Record; modalConfig: Record; } +export interface IMainStoreData extends IStoreData { + globalState: GlobalState; +} + export interface AnimateItem { uid: string; animationName: string; @@ -26,8 +39,12 @@ export interface AnimateItem { animationDelay: number; animationIterationCount: string; animationTimingFunction: string; - isCustom?: boolean; - customKeyFrame?: string; +} + +export interface CustomAnimateObj { + displayName: string; + animateName: string; + keyframe: string; } export interface IBlockType { diff --git a/packages/dooringx-lib/src/core/storeChanger/index.ts b/packages/dooringx-lib/src/core/storeChanger/index.ts index ea09d8e..cd9c010 100644 --- a/packages/dooringx-lib/src/core/storeChanger/index.ts +++ b/packages/dooringx-lib/src/core/storeChanger/index.ts @@ -82,6 +82,12 @@ export class StoreChanger { return this.map[ORIGIN]; } + /** + * 判断是否在编辑模式。 + * 一次也没进行编辑时,storeChanger中未存store,所以只能判断去获取。 + * @return {*} + * @memberof StoreChanger + */ isEdit() { if (storeChangerState.modalEditName !== '') { return true;