Estado de edição de sprite e estado de renderização
Agora, os vários recursos de edição da tela são relativamente ricos, mas são todos recursos gerais de edição.Se alguns sprites quiserem algumas funções especiais de edição, eles não podem fazê-lo por enquanto. Por exemplo:
- O assistente de rich text deseja exibir uma barra de ferramentas para editar a formatação do texto
- O sprite da imagem deseja exibir a caixa de entrada para configurar o endereço do link da imagem
- Alguns componentes sprite primeiro exibem um formulário de configuração simples e assim por diante (sugerimos que muitos itens de configuração complexos sejam implementados exibindo um painel de configuração de propriedade à direita, que está fora do escopo desta discussão)
Então, definimos que o sprite existe 渲染模式
e 编辑模式
, por padrão, o sprite está no estado de renderização e o transformará no estado de edição naquele 双击精灵
momento .
Esteja ele no estado de edição ou não, passamos um editing
parâmetro para o componente sprite, e o próprio sprite controla qual conteúdo é exibido no estado de renderização e no estado de edição, respectivamente.
- O núcleo do editor vincula o ouvinte de evento de clique duplo e define o sprite que está sendo editado. Ao renderizar o sprite, se o sprite está sendo editado é passado como props
export class GraphicEditorCore extends React.Component<IProps, IState> {
state = {
editingSprite,
// 其他属性...
};
componentDidMount() {
document.addEventListener('dblclick', this.handleDoubleClick);
}
handleDoubleClick = (e: MouseEvent) => {
if (!isClickOnSprite(e)) {
this.setState({ editingSprite: null });
return;
}
const editingSprite = getSpriteByEvent(e);
this.setState({ editingSprite });
};
// 递归渲染精灵的方法(这里只放表达将编辑态传入精灵的伪代码)
renderSprite = (sprite: ISprite) => {
const { editingSprite } = this.state;
const editing = editingSprite?.id && editingSprite?.id === sprite.id;
return (
<Sprite key={sprite.id} sprite={sprite}>
<SpriteComponent sprite={sprite} editing={editing} />
</Sprite>
);
}
}
复制代码
- Usando sprites de texto como um exemplo para ilustrar a edição de sprites
const TextEditor = ({ content, onChange }) => {
return (
// 使用foreignObject包裹,即可实现在svg内显示普通html标签
<foreignObject x={0} y={0} width={width} height={height}>
<input
value={content}
onChange={(e) => onChange(e.target.value)}
/>
</foreignObject>
);
};
export class TextSprite extends BaseSprite<IProps> {
render() {
const { sprite, editing } = this.props;
const { content } = sprite.props;
const { updateSpriteProps } = this.props.stage.apis;
// 编辑态
if (editing) {
return (
<TextEditor
content={content}
onChange={value => updateSpriteProps(sprite, { content: value })}
/>
);
}
// 渲染态
return (
<text>{content}</text>
);
}
}
复制代码
alguns sprites comumente usados
1. Texto avançado
import React from 'react';
import type { ISpriteMeta } from '../../interface';
import { BaseSprite } from '../BaseSprite';
const RichTextEditor = ({ content, onChange }) => {
return (
// 使用foreignObject包裹,即可实现在svg内显示普通html标签
<foreignObject
{...props}
x={0}
y={0}
width={width}
height={height}
style={{
width: `${width}px`,
height: `${height}px`,
border: '1px solid #aaa',
padding: '3px 5px',
userSelect: 'none',
}}>
<div
style={{ height: '100%', outline: 'none' }}
contentEditable={editing}
dangerouslySetInnerHTML={{ __html: props.content }}></div>
</foreignObject>
);
};
interface IProps {
width: number;
height: number;
content: string;
}
const SpriteType = 'TextSprite';
export class TextSprite extends BaseSprite<IProps> {
handleChange = (prop: string, value: string) => {
const { sprite, stage } = this.props;
stage.apis.updateSpriteProps(sprite, { [prop]: value });
};
render() {
const { sprite, editing } = this.props;
const { props, attrs } = sprite;
const { width, height } = attrs.size;
const { updateSpriteProps } = this.props.stage.apis;
return (
<RichTextEditor
content={content}
onChange={value => updateSpriteProps(sprite, { content: value })}
/>
);
}
}
export const TextSpriteMeta: ISpriteMeta<IProps> = {
type: SpriteType,
spriteComponent: TextSprite,
initProps: {
width: 100,
height: 40,
content: '',
},
};
export default TextSpriteMeta;
复制代码
2. Imagens
import React from 'react';
import { BaseSprite } from '../BaseSprite';
import type { ISpriteMeta } from '../../interface';
import './index.less';
const ImageSrcInput = ({ content, onChange }) => {
return (
<foreignObject
width={300}
height={30}
x={0}
y={0}
style={{ padding: '0 4px', overflow: 'visible' }}>
<label>src</label>:
<input
value={url}
onChange={(e) => onChange(e.target.value)}
/>
</foreignObject>
);
};
interface IProps {
url: string;
}
const SpriteType = 'ImageSprite';
export class ImageSprite extends BaseSprite<IProps> {
render() {
const { sprite, editing } = this.props;
const { props, attrs } = sprite;
const { width, height } = attrs.size;
const { url } = props;
const { updateSpriteProps } = this.props.stage.apis;
return (
<g className="image-sprite-content">
<image xlinkHref={url} x={0} y={0} width={width} height={height} />
// 编辑态
{editing && (
<TextEditor
content={content}
onChange={value => updateSpriteProps(sprite, { url: value })}
/>
)}
</g>
);
}
}
export const ImageSpriteMeta: ISpriteMeta<IProps> = {
type: SpriteType,
spriteComponent: ImageSprite,
operation: {
resizeLock: true,
},
initProps: {
url: '/img',
},
};
export default ImageSpriteMeta;
复制代码
3. Links
import React from 'react';
import type { IDefaultGraphicProps, ISpriteMeta } from '../../interface';
import { BaseSprite } from '../BaseSprite';
import './index.less';
const LinkEditor = ({ href, target, text, onChange }) => {
return (
<foreignObject
width={width}
height={height}
x={0}
y={0}
style={{ padding: '0 4px', overflow: 'visible' }}>
<a
className="link-sprite-content"
href={href}
target={target}
style={{ color: '#1890ff' }}>
{text}
</a>
<div
className="link-sprite-panel"
style={{ top: `${height + 5}px` }}
onMouseDown={(e: any) => e.stopPropagation()}>
<div className="link-sprite-row-item">
<label className="link-sprite-row-label">文字</label>:
<input
className="link-sprite-row-input"
value={text}
onChange={(e: any) =>
onChange('text', e.target.value)
}
/>
</div>
<div className="link-sprite-row-item">
<label className="link-sprite-row-label">链接</label>:
<input
className="link-sprite-row-input"
value={href}
onChange={(e: any) =>
onChange('href', e.target.value)
}
/>
</div>
<div className="link-sprite-row-item">
<label className="link-sprite-row-label">新页面打开</label>:
<input
className="link-sprite-row-radio"
type="radio"
name="target"
value={target}
checked={target === '_blank'}
onChange={() => onChange('target', '_blank')}
/>
是
<input
className="link-sprite-row-radio"
type="radio"
name="target"
style={{ marginLeft: '10px' }}
value={target}
checked={target === '_self'}
onChange={() => onChange('target', '_self')}
/>
否
{/* <div className="button-container primary-button-container">确定</div> */}
</div>
</div>
</foreignObject>
);
}
interface IProps extends IDefaultGraphicProps {
href: string;
text: string;
target?: '_blank' | '_self' | '_parent' | '_top';
}
const SpriteType = 'LinkSprite';
export class LinkSprite extends BaseSprite<IProps> {
handleChange = (name: string, value: string) => {
const { sprite, stage } = this.props;
const { updateSpriteProps } = stage.apis;
updateSpriteProps(sprite, { [name]: value });
};
render() {
const { sprite, editing } = this.props;
const { props, attrs } = sprite;
const { size, coordinate } = attrs;
const { width, height } = size;
const { x, y } = coordinate;
const { href = '', text = '', target = '_self' } = props;
if (editing) {
return (
<LinkEditor
href={href}
target={target}
text={text}
onChange={this.handleChange}
/>
);
}
return (
<>
<a xlinkHref={href} target="new" style={{ userSelect: 'none' }}>
<text x={x + 4} y={y + 16} fill="#1890ff" dominantBaseline="end">{text}</text>
</a>
</>
);
}
}
export const LinkSpriteMeta: ISpriteMeta<IProps> = {
type: SpriteType,
spriteComponent: LinkSprite,
initProps: {
href: '',
text: '链接',
target: '_blank',
},
};
export default LinkSpriteMeta;
复制代码
Outras sugestões comuns de sprites gráficos
Incentive todos a desenvolver muitos sprites úteis com base nisso e, em seguida, compartilhe-os na área de comentários ~
Alunos que não estão familiarizados com o desenho svg podem aprender aqui: Tutorial SVG - Tutorial para iniciantes
-
Existem vários métodos comuns para desenho svg
-
Um exemplo de svg que pode ser visualizado online
Em geral:
- Palavra
- foto
- Link
- áudio
- vídeo
- folha
forma:
- retângulo
- retângulo arredondado
- apontar
- triângulo
- oval
- paralelogramo
- diamante
- Pentágono
- hexágono
- setor
- polígono livre
- cubo
- Cilindro
Classe de linha:
- segmento de linha
- raios
- linha reta
- linha quebrada
- curva de Bezier de segunda ordem
- Curva de Bézier de terceira ordem
- Curva Suave (conecte os pontos do gráfico de linha com uma curva suave)
- Curvas de caminho livre (curvas desenhadas com o mouse)
Tipo de linha de conexão:
- cabo
- conector de ângulo reto
- conector de curva suave
outro:
- Carrossel
- página iframe
- temporizador, contagem regressiva
- relógio
- fórmula (baseada em LaTex)
- bloco de destaque de código
- outro...