svg implementa a série de editores gráficos nove: o estado de edição de sprites e o desenvolvimento de sprites comuns

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 editingparâ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 ~

imagem.png

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 svgimagem.png

  • Um exemplo de svg que pode ser visualizado online

imagem.png

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...

Acho que você gosta

Origin juejin.im/post/7215620087825481784
Recomendado
Clasificación