美文网首页
基于Draftjs实现的Electron富文本聊天输入框(二)

基于Draftjs实现的Electron富文本聊天输入框(二)

作者: charles0427 | 来源:发表于2018-02-11 15:24 被阅读554次

    图文

    显示

    1. 以Entity的格式添加图片:

      // 通过新建一个Entity,插入图片
        appendImage(image) {
          const {editorState} = this.state;
          const contentState = editorState.getCurrentContent();
          const imageSrc = image.data || image.file.path;
      
          // AtomicBlockUtils插入图片
          const contentStateWithEntity = contentState.createEntity(
            'image',
            'IMMUTABLE',
            {src: imageSrc, image},
          );
          const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
      
          const newEditorState = EditorState.set(
            editorState,
            {currentContent: contentStateWithEntity}
          );
          const newEditorStateWithAtomic = AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, ' ');
          this.setState({
            editorState: newEditorStateWithAtomic,
          });
        }
      

      通过AtomicBlockUtil创建的entity有一些问题:当前为空行时,图片会自动插入下一行; focus、删除等操作不是很正常;

    2. 通过blockRenderFn属性传入自定义的图片组件

      // 自定义block样式
        blockRendererFn(contentBlock) {
          const type = contentBlock.getType();
          let result = null;
      
          if (type === 'atomic') {
            result = {
              component: DraftImage,
              editable: false,
            };
          }
          return result;
        }
      

      对contentBlock类型为atomic指定了自定义组件DraftImage

    3. image.onload()方法中获取图片原尺寸,根据输入框大小限定显示的图片尺寸

      import React from 'react';
      import cs from 'classnames';
      
      export default class DraftImage extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            src: '',
            isActive: false,
          };
        }
      
        componentDidMount() {
          this.setImageSrc(this.props);
        }
      
        componentWillReceiveProps(nextProps) {
          this.setImageSrc(nextProps);
        }
      
        setImageSrc(props) {
          const {block, contentState} = props;
          const key = block.getEntityAt(0);
          if (!key) {
            return '';
          }
          const entity = contentState.getEntity(key);
          if (entity && entity.getType() === 'image') {
            const data = entity.getData();
            const image = new Image();
            const imgSrc = data.src;
            image.onload = () => {
              const size = this.getImageSize(image);
              this.setState({
                src: imgSrc,
                width: size.width + 'px',
                height: size.height + 'px',
              });
            };
            image.src = imgSrc;
          }
        }
      
        handleClick(e) {
          e.stopPropagation();
          this.setState({
            isActive: !this.state.isActive,
          });
        }
      
        // 根据输入框宽高限制图片
        getImageSize(image) {
          const origWidth = image.width;
          const origHeight = image.height;
          const editorWrapperRect = document.getElementsByClassName('chat-draft-editor')[0].getBoundingClientRect();
          const maxWidth = editorWrapperRect.width - 10;
          const maxHeight = editorWrapperRect.height - 10;
          let newWidth = '';
          let newHeight = '';
          let ratio = 1;
          if (origHeight <= maxHeight && origWidth <= maxWidth) {
            newHeight = origHeight;
            newWidth = origWidth;
          }
          if (origHeight <= maxHeight && origWidth > maxWidth) {
            ratio = origWidth / maxWidth;
            newHeight = origHeight / ratio;
            newWidth = maxWidth;
          }
          if (origHeight > maxHeight) {
            ratio = origHeight / maxHeight;
            newWidth = origWidth / ratio;
            newHeight = maxHeight;
          }
          return {width: newWidth, height: newHeight};
        }
      
        render() {
          const imgCls = cs({
            'draft-editor-image': true,
            'img-active': this.state.isActive,
          });
          if (this.state.src !== '' ) {
            return (
              <img
                className={imgCls}
                style={{width: this.state.width, height: this.state.height}}
                src={this.state.src}
                onClick={this.handleClick.bind(this)}
              />
            );
          } else {
            return null;
          }
        }
      }
      
      
    4. 通过监听onPastedFiles(files)方法处理粘贴的文件

      handlePastedFiles(files) {
          files.forEach((blob) => {
            if (blob.type.startsWith('image/')) {
              const image = clipboard.readImage();
      
              const url = image.isEmpty()? '': image.toDataURL();
              this.appendImage({
                data: url,
              });
            }
          });
          return 'handled';
        }
      

      Draftjs这个方法提供的files对象数据不够,只用来判断type是否为图片,读取数据仍使用的clipboard

    发送

    1. 消息发送后,需要将文本框置空

      这里有个bug,清空输入框文本时,不能立即Focus,否则内容不会被清空

    2. 为保证图文消息的发送顺序与输入框编辑时一致,对数组遍历时,设置固定延时:

    相关文章

      网友评论

          本文标题:基于Draftjs实现的Electron富文本聊天输入框(二)

          本文链接:https://www.haomeiwen.com/subject/rsnktftx.html