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

图文 显示

  1. 以Entity的格式添加图片:
    // 通过新建一个Entity,插入图片 appendImage(image) { const {editorState} = this.state; const contentState = editorState.getCurrentContent(); const imageSrc = https://www.it610.com/article/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 = https://www.it610.com/article/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 = https://www.it610.com/article/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 ( ); } 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. 消息发送后,需要将文本框置空
    【基于Draftjs实现的Electron富文本聊天输入框(二)|基于Draftjs实现的Electron富文本聊天输入框(二) —— 图文输入】这里有个bug,清空输入框文本时,不能立即Focus,否则内容不会被清空
  2. 为保证图文消息的发送顺序与输入框编辑时一致,对数组遍历时,设置固定延时:

    推荐阅读