mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
feat: amis支持进入\强调\退出动画配置 (#11091)
* feat: amis支持进入\强调\退出动画配置 * chore: 增加样式管理器 * bugfix * 增加编辑器配置面板 * 动画增加播放功能 * 调整下文案 * bugfix * bugfix --------- Co-authored-by: qinhaoyan <30946345+qinhaoyan@users.noreply.github.com>
This commit is contained in:
parent
7767ab7df0
commit
434a328619
@ -18,7 +18,13 @@ import {IScopedContext, ScopedContext} from './Scoped';
|
|||||||
import {Schema, SchemaNode} from './types';
|
import {Schema, SchemaNode} from './types';
|
||||||
import {DebugWrapper} from './utils/debug';
|
import {DebugWrapper} from './utils/debug';
|
||||||
import getExprProperties from './utils/filter-schema';
|
import getExprProperties from './utils/filter-schema';
|
||||||
import {anyChanged, chainEvents, autobind, TestIdBuilder} from './utils/helper';
|
import {
|
||||||
|
anyChanged,
|
||||||
|
chainEvents,
|
||||||
|
autobind,
|
||||||
|
TestIdBuilder,
|
||||||
|
formateId
|
||||||
|
} from './utils/helper';
|
||||||
import {SimpleMap} from './utils/SimpleMap';
|
import {SimpleMap} from './utils/SimpleMap';
|
||||||
import {bindEvent, dispatchEvent, RendererEvent} from './utils/renderer-event';
|
import {bindEvent, dispatchEvent, RendererEvent} from './utils/renderer-event';
|
||||||
import {isAlive} from 'mobx-state-tree';
|
import {isAlive} from 'mobx-state-tree';
|
||||||
@ -28,6 +34,9 @@ import {buildStyle} from './utils/style';
|
|||||||
import {isExpression} from './utils/formula';
|
import {isExpression} from './utils/formula';
|
||||||
import {StatusScopedProps} from './StatusScoped';
|
import {StatusScopedProps} from './StatusScoped';
|
||||||
import {evalExpression, filter} from './utils/tpl';
|
import {evalExpression, filter} from './utils/tpl';
|
||||||
|
import {CSSTransition} from 'react-transition-group';
|
||||||
|
import {createAnimationStyle} from './utils/animations';
|
||||||
|
import styleManager from './StyleManager';
|
||||||
|
|
||||||
interface SchemaRendererProps
|
interface SchemaRendererProps
|
||||||
extends Partial<Omit<RendererProps, 'statusStore'>>,
|
extends Partial<Omit<RendererProps, 'statusStore'>>,
|
||||||
@ -89,17 +98,44 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
|||||||
schema: any;
|
schema: any;
|
||||||
path: string;
|
path: string;
|
||||||
|
|
||||||
|
animationTimeout: {
|
||||||
|
enter?: number;
|
||||||
|
exit?: number;
|
||||||
|
} = {};
|
||||||
|
animationClassNames: {
|
||||||
|
appear?: string;
|
||||||
|
enter?: string;
|
||||||
|
exit?: string;
|
||||||
|
} = {};
|
||||||
|
|
||||||
reaction: any;
|
reaction: any;
|
||||||
unbindEvent: (() => void) | undefined = undefined;
|
unbindEvent: (() => void) | undefined = undefined;
|
||||||
isStatic: any = undefined;
|
isStatic: any = undefined;
|
||||||
|
|
||||||
constructor(props: SchemaRendererProps) {
|
constructor(props: SchemaRendererProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
const animations = props?.schema?.animations;
|
||||||
|
if (animations) {
|
||||||
|
let id = props?.schema.id;
|
||||||
|
id = formateId(id);
|
||||||
|
if (animations.enter) {
|
||||||
|
this.animationTimeout.enter = (animations.enter.duration || 1) * 1000;
|
||||||
|
this.animationClassNames.enter = `${animations.enter.type}-${id}-enter`;
|
||||||
|
this.animationClassNames.appear = this.animationClassNames.enter;
|
||||||
|
}
|
||||||
|
if (animations.exit) {
|
||||||
|
this.animationTimeout.exit = (animations.exit.duration || 1) * 1000;
|
||||||
|
this.animationClassNames.exit = `${animations.exit.type}-${id}-exit`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.refFn = this.refFn.bind(this);
|
this.refFn = this.refFn.bind(this);
|
||||||
this.renderChild = this.renderChild.bind(this);
|
this.renderChild = this.renderChild.bind(this);
|
||||||
this.reRender = this.reRender.bind(this);
|
this.reRender = this.reRender.bind(this);
|
||||||
this.resolveRenderer(this.props);
|
this.resolveRenderer(this.props);
|
||||||
this.dispatchEvent = this.dispatchEvent.bind(this);
|
this.dispatchEvent = this.dispatchEvent.bind(this);
|
||||||
|
this.addAnimationAttention = this.addAnimationAttention.bind(this);
|
||||||
|
this.removeAnimationAttention = this.removeAnimationAttention.bind(this);
|
||||||
|
|
||||||
// 监听statusStore更新
|
// 监听statusStore更新
|
||||||
this.reaction = reaction(
|
this.reaction = reaction(
|
||||||
@ -121,9 +157,18 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount(): void {
|
||||||
|
if (this.props.schema.animations) {
|
||||||
|
let {animations, id} = this.props.schema;
|
||||||
|
id = formateId(id);
|
||||||
|
createAnimationStyle(id, animations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.reaction?.();
|
this.reaction?.();
|
||||||
this.unbindEvent?.();
|
this.unbindEvent?.();
|
||||||
|
this.removeAnimationStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 限制:只有 schema 除外的 props 变化,或者 schema 里面的某个成员值发生变化才更新。
|
// 限制:只有 schema 除外的 props 变化,或者 schema 里面的某个成员值发生变化才更新。
|
||||||
@ -154,6 +199,14 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeAnimationStyle() {
|
||||||
|
if (this.props.schema.animations) {
|
||||||
|
let {id} = this.props.schema;
|
||||||
|
id = formateId(id);
|
||||||
|
styleManager.removeStyles(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resolveRenderer(props: SchemaRendererProps, force = false): any {
|
resolveRenderer(props: SchemaRendererProps, force = false): any {
|
||||||
let schema = props.schema;
|
let schema = props.schema;
|
||||||
let path = props.$path;
|
let path = props.$path;
|
||||||
@ -297,6 +350,25 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
|||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addAnimationAttention(node: HTMLElement) {
|
||||||
|
const {schema} = this.props || {};
|
||||||
|
const {attention} = schema?.animations || {};
|
||||||
|
if (attention) {
|
||||||
|
let {id} = schema;
|
||||||
|
id = formateId(id);
|
||||||
|
node.classList.add(`${attention.type}-${id}-attention`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeAnimationAttention(node: HTMLElement) {
|
||||||
|
const {schema} = this.props || {};
|
||||||
|
const {attention} = schema?.animations || {};
|
||||||
|
if (attention) {
|
||||||
|
let {id} = schema;
|
||||||
|
id = formateId(id);
|
||||||
|
node.classList.remove(`${attention.type}-${id}-attention`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render(): JSX.Element | null {
|
render(): JSX.Element | null {
|
||||||
let {
|
let {
|
||||||
$path: _,
|
$path: _,
|
||||||
@ -447,6 +519,8 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
|||||||
} = schema;
|
} = schema;
|
||||||
const Component = renderer.component!;
|
const Component = renderer.component!;
|
||||||
|
|
||||||
|
let animationIn = true;
|
||||||
|
|
||||||
// 原来表单项的 visible: false 和 hidden: true 表单项的值和验证是有效的
|
// 原来表单项的 visible: false 和 hidden: true 表单项的值和验证是有效的
|
||||||
// 而 visibleOn 和 hiddenOn 是无效的,
|
// 而 visibleOn 和 hiddenOn 是无效的,
|
||||||
// 这个本来就是个bug,但是已经被广泛使用了
|
// 这个本来就是个bug,但是已经被广泛使用了
|
||||||
@ -458,8 +532,12 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
|||||||
!renderer.isFormItem ||
|
!renderer.isFormItem ||
|
||||||
(schema.visible !== false && !schema.hidden))
|
(schema.visible !== false && !schema.hidden))
|
||||||
) {
|
) {
|
||||||
|
if (schema.animations) {
|
||||||
|
animationIn = false;
|
||||||
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// withStore 里面会处理,而且会实时处理
|
// withStore 里面会处理,而且会实时处理
|
||||||
// 这里处理反而导致了问题
|
// 这里处理反而导致了问题
|
||||||
@ -526,12 +604,28 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const component = supportRef ? (
|
let component = supportRef ? (
|
||||||
<Component {...props} ref={this.childRef} />
|
<Component {...props} ref={this.childRef} />
|
||||||
) : (
|
) : (
|
||||||
<Component {...props} forwardedRef={this.childRef} />
|
<Component {...props} forwardedRef={this.childRef} />
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (schema.animations) {
|
||||||
|
component = (
|
||||||
|
<CSSTransition
|
||||||
|
in={animationIn}
|
||||||
|
timeout={this.animationTimeout}
|
||||||
|
classNames={this.animationClassNames}
|
||||||
|
onEntered={this.addAnimationAttention}
|
||||||
|
onExit={this.removeAnimationAttention}
|
||||||
|
appear
|
||||||
|
unmountOnExit
|
||||||
|
>
|
||||||
|
{component}
|
||||||
|
</CSSTransition>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return this.props.env.enableAMISDebug ? (
|
return this.props.env.enableAMISDebug ? (
|
||||||
<DebugWrapper renderer={renderer}>{component}</DebugWrapper>
|
<DebugWrapper renderer={renderer}>{component}</DebugWrapper>
|
||||||
) : (
|
) : (
|
||||||
|
63
packages/amis-core/src/StyleManager.ts
Normal file
63
packages/amis-core/src/StyleManager.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import kebabCase from 'lodash/kebabCase';
|
||||||
|
|
||||||
|
interface Style {
|
||||||
|
[id: string]: {
|
||||||
|
[className: string]: {
|
||||||
|
[propName: string]: string | number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class StyleManager {
|
||||||
|
styles: Style;
|
||||||
|
styleDom: HTMLStyleElement;
|
||||||
|
styleText: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.styles = {};
|
||||||
|
this.styleDom = document.createElement('style');
|
||||||
|
this.styleDom.id = 'amis-styles';
|
||||||
|
document.head.appendChild(this.styleDom);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStyle(style: Style) {
|
||||||
|
Object.keys(style).forEach(className => {
|
||||||
|
if (!this.styles[className]) {
|
||||||
|
this.styles[className] = style[className];
|
||||||
|
} else {
|
||||||
|
this.styles[className] = {
|
||||||
|
...this.styles[className],
|
||||||
|
...style[className]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.updateStyleDom();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeStyles(id: string) {
|
||||||
|
delete this.styles[id];
|
||||||
|
this.updateStyleDom();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStyleDom() {
|
||||||
|
const styleText = Object.keys(this.styles)
|
||||||
|
.map(id => {
|
||||||
|
const style = this.styles[id];
|
||||||
|
return Object.keys(style)
|
||||||
|
.map(className => {
|
||||||
|
return `${className} {${Object.keys(style[className])
|
||||||
|
.map(propName => {
|
||||||
|
return `${kebabCase(propName)}: ${style[className][propName]};`;
|
||||||
|
})
|
||||||
|
.join('')}}`;
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
|
|
||||||
|
this.styleDom.innerHTML = styleText;
|
||||||
|
this.styleText = styleText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new StyleManager();
|
@ -36,6 +36,7 @@ import './polyfills';
|
|||||||
import './renderers/builtin';
|
import './renderers/builtin';
|
||||||
import './renderers/register';
|
import './renderers/register';
|
||||||
export * from './utils/index';
|
export * from './utils/index';
|
||||||
|
export * from './utils/animations';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
export * from './store';
|
export * from './store';
|
||||||
import * as utils from './utils/helper';
|
import * as utils from './utils/helper';
|
||||||
@ -123,6 +124,8 @@ import type {IItem} from './store/list';
|
|||||||
import CustomStyle from './components/CustomStyle';
|
import CustomStyle from './components/CustomStyle';
|
||||||
import {StatusScoped} from './StatusScoped';
|
import {StatusScoped} from './StatusScoped';
|
||||||
|
|
||||||
|
import styleManager from './StyleManager';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const version = '__buildVersion';
|
export const version = '__buildVersion';
|
||||||
(window as any).amisVersionInfo = {
|
(window as any).amisVersionInfo = {
|
||||||
@ -131,6 +134,7 @@ export const version = '__buildVersion';
|
|||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
styleManager,
|
||||||
clearStoresCache,
|
clearStoresCache,
|
||||||
updateEnv,
|
updateEnv,
|
||||||
Renderer,
|
Renderer,
|
||||||
|
@ -3,6 +3,7 @@ import type {JSONSchema7} from 'json-schema';
|
|||||||
import {ListenerAction} from './actions/Action';
|
import {ListenerAction} from './actions/Action';
|
||||||
import {debounceConfig, trackConfig} from './utils/renderer-event';
|
import {debounceConfig, trackConfig} from './utils/renderer-event';
|
||||||
import type {TestIdBuilder} from './utils/helper';
|
import type {TestIdBuilder} from './utils/helper';
|
||||||
|
import {AnimationsProps} from './utils/animations';
|
||||||
|
|
||||||
export interface Option {
|
export interface Option {
|
||||||
/**
|
/**
|
||||||
@ -307,6 +308,7 @@ export interface Schema {
|
|||||||
static?: boolean;
|
static?: boolean;
|
||||||
children?: JSX.Element | ((props: any, schema?: any) => JSX.Element) | null;
|
children?: JSX.Element | ((props: any, schema?: any) => JSX.Element) | null;
|
||||||
definitions?: Definitions;
|
definitions?: Definitions;
|
||||||
|
animations?: AnimationsProps;
|
||||||
[propName: string]: any;
|
[propName: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
86
packages/amis-core/src/utils/animations.ts
Normal file
86
packages/amis-core/src/utils/animations.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import styleManager from '../StyleManager';
|
||||||
|
|
||||||
|
export interface AnimationsProps {
|
||||||
|
enter?: {
|
||||||
|
type: string;
|
||||||
|
duration?: number;
|
||||||
|
};
|
||||||
|
attention?: {
|
||||||
|
type: string;
|
||||||
|
duration?: number;
|
||||||
|
repeat?: string;
|
||||||
|
delay?: number;
|
||||||
|
};
|
||||||
|
exit?: {
|
||||||
|
type: string;
|
||||||
|
duration?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateStyleByAnimation(
|
||||||
|
className: string[],
|
||||||
|
animation: {
|
||||||
|
name: string;
|
||||||
|
duration?: number;
|
||||||
|
iterationCount?: string;
|
||||||
|
delay?: number;
|
||||||
|
fillMode?: string;
|
||||||
|
timingFunction?: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
[className.join(',')]: {
|
||||||
|
animationName: animation.name,
|
||||||
|
animationDuration: `${animation.duration || 1}s`,
|
||||||
|
animationIterationCount: animation.iterationCount || 1,
|
||||||
|
animationDelay: `${animation.delay || 0}s`,
|
||||||
|
animationTimingFunction: animation.timingFunction || 'ease',
|
||||||
|
animationFillMode: animation.fillMode || 'none'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAnimationStyle(
|
||||||
|
id: string,
|
||||||
|
animationsConfig: AnimationsProps
|
||||||
|
) {
|
||||||
|
const enterAnimationConfig = animationsConfig.enter;
|
||||||
|
let enterStyle = {};
|
||||||
|
if (enterAnimationConfig?.type) {
|
||||||
|
enterStyle = generateStyleByAnimation(
|
||||||
|
[`.${enterAnimationConfig.type}-${id}-enter`],
|
||||||
|
{name: enterAnimationConfig.type, duration: enterAnimationConfig.duration}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const attentionAnimationConfig = animationsConfig.attention;
|
||||||
|
let attentionStyle = {};
|
||||||
|
if (attentionAnimationConfig?.type) {
|
||||||
|
attentionStyle = generateStyleByAnimation(
|
||||||
|
[`.${attentionAnimationConfig.type}-${id}-attention`],
|
||||||
|
{
|
||||||
|
name: attentionAnimationConfig.type,
|
||||||
|
duration: attentionAnimationConfig.duration,
|
||||||
|
iterationCount: attentionAnimationConfig.repeat || 'infinite',
|
||||||
|
delay: attentionAnimationConfig.delay
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const exitAnimationConfig = animationsConfig.exit;
|
||||||
|
let exitStyle = {};
|
||||||
|
if (exitAnimationConfig?.type) {
|
||||||
|
exitStyle = generateStyleByAnimation(
|
||||||
|
[`.${exitAnimationConfig.type}-${id}-exit`],
|
||||||
|
{
|
||||||
|
name: exitAnimationConfig.type,
|
||||||
|
duration: exitAnimationConfig.duration,
|
||||||
|
fillMode: 'forwards'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
styleManager.updateStyle({
|
||||||
|
[id]: Object.assign({}, enterStyle, attentionStyle, exitStyle)
|
||||||
|
});
|
||||||
|
}
|
@ -2376,3 +2376,17 @@ export function supportsMjs() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formateId(id: string) {
|
||||||
|
// 将className非法字符替换为短横线
|
||||||
|
id = id.replace(/[^a-zA-Z0-9-]/g, '-');
|
||||||
|
// 将连续的-替换为单个-
|
||||||
|
id = id.replace(/-{2,}/g, '-');
|
||||||
|
// 去掉首尾的-
|
||||||
|
id = id.replace(/^-|-$/g, '');
|
||||||
|
// 首字母不能为数字
|
||||||
|
if (/^\d/.test(id)) {
|
||||||
|
id = 'amis-' + id;
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
@ -790,7 +790,14 @@ export function filterSchemaForEditor(schema: any): any {
|
|||||||
Object.keys(schema).forEach(key => {
|
Object.keys(schema).forEach(key => {
|
||||||
const value = schema[key];
|
const value = schema[key];
|
||||||
if (
|
if (
|
||||||
~['visible', 'visibleOn', 'hidden', 'hiddenOn', 'toggled'].indexOf(key)
|
~[
|
||||||
|
'visible',
|
||||||
|
'visibleOn',
|
||||||
|
'hidden',
|
||||||
|
'hiddenOn',
|
||||||
|
'toggled',
|
||||||
|
'animations' // 编辑态也不能有动画
|
||||||
|
].indexOf(key)
|
||||||
) {
|
) {
|
||||||
key = `$$${key}`;
|
key = `$$${key}`;
|
||||||
modified = true;
|
modified = true;
|
||||||
|
@ -1,6 +1,448 @@
|
|||||||
import {setSchemaTpl, getSchemaTpl, defaultValue} from 'amis-editor-core';
|
import {setSchemaTpl, getSchemaTpl, defaultValue} from 'amis-editor-core';
|
||||||
import type {SchemaCollection} from 'amis';
|
import {createAnimationStyle, formateId, type SchemaCollection} from 'amis';
|
||||||
import kebabCase from 'lodash/kebabCase';
|
import kebabCase from 'lodash/kebabCase';
|
||||||
|
import {styleManager} from 'amis-core';
|
||||||
|
|
||||||
|
const animationOptions = {
|
||||||
|
enter: [
|
||||||
|
{
|
||||||
|
label: '淡入',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '淡入',
|
||||||
|
value: 'fadeIn'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeInDown',
|
||||||
|
label: '从上淡入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeInDownBig',
|
||||||
|
label: '从上淡入(加强效果)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeInLeft',
|
||||||
|
label: '从左淡入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeInLeftBig',
|
||||||
|
label: '从左淡入(加强效果)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeInRight',
|
||||||
|
label: '从右淡入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeInRightBig',
|
||||||
|
label: '从右淡入(加强效果)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeInUp',
|
||||||
|
label: '从下淡入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeInUpBig',
|
||||||
|
label: '从下淡入(加强效果)'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '回弹',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'backInDown',
|
||||||
|
label: '从上回弹进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'backInLeft',
|
||||||
|
label: '从左回弹进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'backInRight',
|
||||||
|
label: '从右回弹进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'backInUp',
|
||||||
|
label: '从下回弹进入'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '旋转',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'rotateIn',
|
||||||
|
label: '旋转进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rotateInDownLeft',
|
||||||
|
label: '左上角旋转进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rotateInDownRight',
|
||||||
|
label: '右上角旋转进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rotateInUpLeft',
|
||||||
|
label: '左下角旋转进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rotateInUpRight',
|
||||||
|
label: '右下角旋转进入'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '滑动',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'slideInUp',
|
||||||
|
label: '从下滑入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'slideInDown',
|
||||||
|
label: '从上滑入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'slideInLeft',
|
||||||
|
label: '从左滑入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'slideInRight',
|
||||||
|
label: '从右滑入'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '翻页',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'flip',
|
||||||
|
label: '翻页'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'flipInY',
|
||||||
|
label: '水平翻页'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'flipInX',
|
||||||
|
label: '垂直翻页'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '弹跳',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'bounceIn',
|
||||||
|
label: '弹跳进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'bounceInDown',
|
||||||
|
label: '从上弹跳进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'bounceInLeft',
|
||||||
|
label: '从左弹跳进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'bounceInRight',
|
||||||
|
label: '从右弹跳进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'bounceInUp',
|
||||||
|
label: '从下弹跳进入'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '缩放',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'zoomIn',
|
||||||
|
label: '缩放进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'zoomInDown',
|
||||||
|
label: '从上缩放进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'zoomInLeft',
|
||||||
|
label: '从左缩放进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'zoomInRight',
|
||||||
|
label: '从右缩放进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'zoomInUp',
|
||||||
|
label: '从下缩放进入'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '其他',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'lightSpeedInLeft',
|
||||||
|
label: '从左光速进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'lightSpeedInRight',
|
||||||
|
label: '从右光速进入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rollIn',
|
||||||
|
label: '滚动进入'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
attention: [
|
||||||
|
{
|
||||||
|
label: '弹跳',
|
||||||
|
value: 'bounce'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '闪烁',
|
||||||
|
value: 'flash'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'headShake',
|
||||||
|
label: '摇头'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'heartBeat',
|
||||||
|
label: '心跳'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'jello',
|
||||||
|
label: '果冻'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '跳动',
|
||||||
|
value: 'pulse'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '摇摆',
|
||||||
|
value: 'swing'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '震动',
|
||||||
|
value: 'tada'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '晃动',
|
||||||
|
value: 'wobble'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '抖动',
|
||||||
|
value: 'shake'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'shakeX',
|
||||||
|
label: '水平抖动'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'shakeY',
|
||||||
|
label: '垂直抖动'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rubberBand',
|
||||||
|
label: '橡皮筋'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
exit: [
|
||||||
|
{
|
||||||
|
label: '淡出',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '淡出',
|
||||||
|
value: 'fadeOut'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeOutDown',
|
||||||
|
label: '向下淡出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeOutDownBig',
|
||||||
|
label: '向下淡出(加强效果)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeOutLeft',
|
||||||
|
label: '向左淡出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeOutLeftBig',
|
||||||
|
label: '向左淡出(加强效果)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeOutRight',
|
||||||
|
label: '向右淡出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeOutRightBig',
|
||||||
|
label: '向右淡出(加强效果)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeOutUp',
|
||||||
|
label: '向上淡出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'fadeOutUpBig',
|
||||||
|
label: '向上淡出(加强效果)'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '回弹',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'backOutDown',
|
||||||
|
label: '向下回弹退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'backOutLeft',
|
||||||
|
label: '向左回弹退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'backOutRight',
|
||||||
|
label: '向右回弹退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'backOutUp',
|
||||||
|
label: '向上回弹退出'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '旋转',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'rotateOut',
|
||||||
|
label: '旋转退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rotateOutDownLeft',
|
||||||
|
label: '左上角旋转退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rotateOutDownRight',
|
||||||
|
label: '右上角旋转退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rotateOutUpLeft',
|
||||||
|
label: '左下角旋转退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rotateOutUpRight',
|
||||||
|
label: '右下角旋转退出'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '滑动',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'slideOutUp',
|
||||||
|
label: '向上滑入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'slideOutDown',
|
||||||
|
label: '向下滑入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'slideOutLeft',
|
||||||
|
label: '向左滑入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'slideOutRight',
|
||||||
|
label: '向右滑入'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '翻页',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'flipOutY',
|
||||||
|
label: '水平翻页'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'flipOutX',
|
||||||
|
label: '垂直翻页'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '弹跳',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'bounceOut',
|
||||||
|
label: '弹跳退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'bounceOutDown',
|
||||||
|
label: '向下弹跳退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'bounceOutLeft',
|
||||||
|
label: '向左弹跳退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'bounceOutRight',
|
||||||
|
label: '向右弹跳退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'bounceOutUp',
|
||||||
|
label: '向上弹跳退出'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '缩放',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'zoomOut',
|
||||||
|
label: '缩放退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'zoomOutDown',
|
||||||
|
label: '向上缩放退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'zoomOutLeft',
|
||||||
|
label: '向左缩放退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'zoomOutRight',
|
||||||
|
label: '向右缩放退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'zoomOutUp',
|
||||||
|
label: '向下缩放退出'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '其他',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'lightSpeedOutLeft',
|
||||||
|
label: '向左光速退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'lightSpeedOutRight',
|
||||||
|
label: '向右光速退出'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rollOut',
|
||||||
|
label: '滚动退出'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
setSchemaTpl('style:formItem', ({renderer, schema}: any) => {
|
setSchemaTpl('style:formItem', ({renderer, schema}: any) => {
|
||||||
return {
|
return {
|
||||||
@ -809,7 +1251,8 @@ setSchemaTpl(
|
|||||||
label: false
|
label: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
getSchemaTpl('animation')
|
||||||
].filter(item => !~exclude.indexOf(item.key || ''));
|
].filter(item => !~exclude.indexOf(item.key || ''));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -845,3 +1288,118 @@ setSchemaTpl(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setSchemaTpl('animation', () => {
|
||||||
|
const animation = (
|
||||||
|
type: 'enter' | 'attention' | 'exit',
|
||||||
|
label: string,
|
||||||
|
schema: any = []
|
||||||
|
) => [
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
name: `animations.${type}`,
|
||||||
|
pipeIn: (value: boolean) => !!value,
|
||||||
|
pipeOut: (value: boolean) => {
|
||||||
|
if (value) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
label
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'container',
|
||||||
|
className: 'm-b ae-ExtendMore',
|
||||||
|
visibleOn: `animations.${type}`,
|
||||||
|
body: [
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
name: `animations.${type}.type`,
|
||||||
|
selectMode: 'group',
|
||||||
|
options: animationOptions[type],
|
||||||
|
label: '类型',
|
||||||
|
selectFirst: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input-number',
|
||||||
|
name: `animations.${type}.duration`,
|
||||||
|
label: '持续',
|
||||||
|
value: 1,
|
||||||
|
suffix: '秒',
|
||||||
|
min: 0,
|
||||||
|
precision: 3
|
||||||
|
},
|
||||||
|
...schema
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'button',
|
||||||
|
visibleOn: `animations.${type}`,
|
||||||
|
className: 'm-b',
|
||||||
|
block: true,
|
||||||
|
level: 'enhance',
|
||||||
|
size: 'sm',
|
||||||
|
label: '播放',
|
||||||
|
onClick: (e: any, {data}: any) => {
|
||||||
|
let doc = document;
|
||||||
|
const isMobile = (window as any).editorStore.isMobile;
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
doc = (document.getElementsByClassName('ae-PreviewIFrame')[0] as any)
|
||||||
|
.contentDocument;
|
||||||
|
}
|
||||||
|
let {id, animations} = data;
|
||||||
|
const el = doc.querySelector(`[name="${id}"]`);
|
||||||
|
id = formateId(id);
|
||||||
|
const className = `${animations[type].type}-${id}-${type}`;
|
||||||
|
el?.classList.add(className);
|
||||||
|
createAnimationStyle(id, animations);
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
let style = doc.getElementById('amis-styles');
|
||||||
|
if (!style) {
|
||||||
|
style = doc.createElement('style');
|
||||||
|
style.id = 'amis-styles';
|
||||||
|
doc.head.appendChild(style);
|
||||||
|
}
|
||||||
|
style.innerHTML = styleManager.styleText;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
el?.classList.remove(className);
|
||||||
|
}, ((animations[type].duration || 1) + (animations[type].delay || 0)) * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: '动画',
|
||||||
|
body: [
|
||||||
|
...animation('enter', '进入动画'),
|
||||||
|
...animation('attention', '强调动画', [
|
||||||
|
{
|
||||||
|
label: '重复',
|
||||||
|
type: 'select',
|
||||||
|
name: 'animations.attention.repeat',
|
||||||
|
value: 'infinite',
|
||||||
|
options: [
|
||||||
|
...[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(i => ({
|
||||||
|
label: i,
|
||||||
|
value: i
|
||||||
|
})),
|
||||||
|
{label: '无限', value: 'infinite'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '延迟',
|
||||||
|
type: 'input-number',
|
||||||
|
name: 'animations.attention.delay',
|
||||||
|
value: 0,
|
||||||
|
suffix: '秒',
|
||||||
|
precision: 3
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
...animation('exit', '退出动画')
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
"@rc-component/mini-decimal": "^1.0.1",
|
"@rc-component/mini-decimal": "^1.0.1",
|
||||||
"amis-core": "^6.8.0",
|
"amis-core": "^6.8.0",
|
||||||
"amis-formula": "^6.8.0",
|
"amis-formula": "^6.8.0",
|
||||||
|
"animate.css": "4.1.1",
|
||||||
"classnames": "2.3.2",
|
"classnames": "2.3.2",
|
||||||
"codemirror": "^5.63.0",
|
"codemirror": "^5.63.0",
|
||||||
"downshift": "6.1.12",
|
"downshift": "6.1.12",
|
||||||
|
@ -7,5 +7,6 @@
|
|||||||
@import '../../../node_modules/video-react/dist/video-react';
|
@import '../../../node_modules/video-react/dist/video-react';
|
||||||
@import '../../../node_modules/cropperjs/dist/cropper';
|
@import '../../../node_modules/cropperjs/dist/cropper';
|
||||||
@import '../../../node_modules/office-viewer/dist/office';
|
@import '../../../node_modules/office-viewer/dist/office';
|
||||||
|
@import '../../../node_modules/animate.css/animate.min';
|
||||||
|
|
||||||
@import './components/react-datetime';
|
@import './components/react-datetime';
|
||||||
|
Loading…
Reference in New Issue
Block a user