当前位置:   article > 正文

小程序H5页面使用富文本quill_富文本编辑器 h5

富文本编辑器 h5
  1. let speakEditor = {};
  2. if (process.env.TARO_ENV === 'h5') {
  3. speakEditor = require('./quill-editor');
  4. }
  5. const SpeakEditor = speakEditor.default || <View />;
  6. // 外层back使用相对定位,这样SpeakEditor使用绝对定位时位置就没问题了
  7. <View className={style.back}>
  8. <SpeakEditor onInput={handleInputChange} value={''} />
  9. </View>
  10. // SpeakEditor组件index.jsx
  11. import {createRef, useCallback, useEffect, useImperativeHandle, useRef} from 'react';
  12. import {Events} from '@tarojs/taro';
  13. import {View} from '@tarojs/components';
  14. import ReactQuill from 'react-quill';
  15. import 'react-quill/dist/quill.snow.css';
  16. import 'tippy.js/dist/tippy.css';
  17. import style from './style.less';
  18. export const events = new Events();
  19. const oneSelfRef = createRef();
  20. export default function SpeakEditor(props) {
  21. const {value, onInput} = props;
  22. const reactQuillRef = useRef();
  23. const quillRef = useRef();
  24. useImperativeHandle(oneSelfRef, () => ({
  25. getText() {
  26. return quillRef.current.getText();
  27. },
  28. getContents() {
  29. return quillRef.current.getContents();
  30. },
  31. loadContents(contents) {
  32. quillRef.current.setContents(contents);
  33. },
  34. editorEvent(eventName, handler) {
  35. quillRef.current.root.addEventListener(eventName, handler);
  36. },
  37. editorOffEvent(eventName, handler) {
  38. quillRef.current.root.removeEventListener(eventName, handler);
  39. },
  40. call(method, ...args) {
  41. return quillRef.current[method](...args);
  42. },
  43. focusOnSelection() {
  44. // 滚动到光标处
  45. const editor = quillRef.current; // quill编辑器实例
  46. const selection = editor.getSelection(); // 获取选中范围
  47. if (selection) {
  48. const [line] = editor.getLines(selection.index, 1);
  49. if (line) {
  50. editor.scrollIntoView(line);
  51. }
  52. }
  53. }
  54. }));
  55. useEffect(() => {
  56. quillRef.current = reactQuillRef.current.getEditor();
  57. }, []);
  58. const handleChange = useCallback(
  59. (_value, delta, source, editor) => {
  60. // console.log('Change: ', {_value, delta, source, editor}, editor.getContents());
  61. onInput({detail: {value: _value}});
  62. },
  63. [onInput]
  64. );
  65. return (
  66. <View className={style.quill_editor}>
  67. <ReactQuill theme="snow" value={value} onChange={handleChange} ref={reactQuillRef} />
  68. </View>
  69. );
  70. }
  71. export function event(eventName, handler) {
  72. if (oneSelfRef.current) {
  73. oneSelfRef.current.call('on', eventName, handler);
  74. } else if (event.counter < 20) {
  75. event.counter++;
  76. setTimeout(()=> {
  77. event(eventName, handler);
  78. }, 200);
  79. }
  80. }
  81. event.counter = 0;
  82. export function offEvent(eventName, handler) {
  83. oneSelfRef.current?.call('off', eventName, handler);
  84. }
  85. export function editorEvent(eventName, handler) {
  86. if (oneSelfRef.current) {
  87. oneSelfRef.current.editorEvent(eventName, handler);
  88. } else if (editorEvent.counter < 20) { editorEvent.counter++; setTimeout(()=> {
  89. oneSelfRef.current.editorEvent(eventName, handler);
  90. }, 200);
  91. }
  92. }
  93. editorEvent.counter = 0;
  94. export function editorOffEvent(eventName, handler) {
  95. oneSelfRef.current.editorOffEvent(eventName, handler);
  96. }
  97. export function on(eventName, handler) {
  98. events.on(eventName, handler);
  99. }
  100. export function off(eventName, handler) {
  101. if (!eventName || !handler) {
  102. events.off();
  103. } else {
  104. events.off(eventName, handler);
  105. }
  106. }
  107. export function getText() {
  108. return oneSelfRef.current?.getText();
  109. }
  110. export function getContents() {
  111. return oneSelfRef.current?.getContents();
  112. }
  113. export function loadContents(contents) {
  114. return oneSelfRef.current?.loadContents(contents);
  115. }
  116. export function doBlur() {
  117. console.log('doBlur');
  118. return oneSelfRef.current?.call('blur');
  119. }
  120. export function doFocus() {
  121. console.log('doFocus');
  122. return oneSelfRef.current?.call('focus');
  123. }
  124. export function focusOnSelection() {
  125. oneSelfRef.current?.focusOnSelection();
  126. }
  127. // SpeakEditor组件style.less
  128. .quill_editor {
  129. position: absolute;
  130. top: 0;
  131. right: 0;
  132. bottom: 0;
  133. left: 0;
  134. width: 100%;
  135. :global {
  136. .quill {
  137. width: 100%;
  138. height: 100%;
  139. user-select: text;
  140. }
  141. .ql-toolbar {
  142. display: none;
  143. }
  144. .ql-container {
  145. font-size: 28px;
  146. color: white;
  147. border: none;
  148. }
  149. .ql-editor {
  150. padding: 0;
  151. line-height: 38px;
  152. letter-spacing: 4px;
  153. }
  154. .pause_embed {
  155. display: inline-block;
  156. margin: 6px 0;
  157. margin-right: 3px;
  158. font-size: 28px !important;
  159. color: #468df7;
  160. pointer-events: auto;
  161. }
  162. .reading_method {
  163. display: inline-block;
  164. padding: 0;
  165. padding-left: 4px;
  166. margin: 6px 0;
  167. margin-right: 3px;
  168. font-size: inherit !important;
  169. font-weight: inherit !important;
  170. pointer-events: none;
  171. // background: rgb(66, 44, 54);
  172. background: rgba(239, 161, 56, 0.32);
  173. border-radius: 4px;
  174. &::after {
  175. display: inline-block;
  176. height: 100%;
  177. padding: 3px 4px;
  178. margin-left: 4px;
  179. font-size: 22px;
  180. color: #efa138;
  181. pointer-events: auto;
  182. background: rgba(239, 161, 56, 0.16);
  183. content: attr(data-str);
  184. }
  185. }
  186. .shift_speed {
  187. display: inline-block;
  188. margin: 6px 0;
  189. margin-right: 3px;
  190. font-size: inherit !important;
  191. font-weight: inherit !important;
  192. pointer-events: none;
  193. &::before {
  194. color: #468df7;
  195. content: '【';
  196. }
  197. &::after {
  198. color: #468df7;
  199. pointer-events: auto;
  200. content: '】' attr(data-value) 'x';
  201. }
  202. }
  203. .chars_check {
  204. display: inline-block;
  205. padding: 0;
  206. padding-left: 4px;
  207. margin: 6px 0;
  208. margin-right: 3px;
  209. font-size: inherit !important;
  210. font-weight: inherit !important;
  211. // pointer-events: none;
  212. background: rgba(82, 196, 26, 0.16);
  213. border-radius: 4px;
  214. &::after {
  215. display: inline-block;
  216. padding: 3px 4px;
  217. margin-bottom: 5px;
  218. margin-left: 4px;
  219. font-size: 22px;
  220. vertical-align: middle;
  221. // pointer-events: auto;
  222. content: attr(data-str);
  223. }
  224. &[data-str=''] {
  225. &::after {
  226. width: 24px;
  227. height: 24px;
  228. margin-right: 7px;
  229. margin-left: 3px;
  230. background: center no-repeat;
  231. background-size: contain;
  232. background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE5LjYxMDUgMTEuODcwMkMxOS42MTA1IDExLjM4OTYgMjAuMDAwMSAxMSAyMC40ODA2IDExSDIzLjk2MTNDMjQuNDQxOCAxMSAyNC44MzE0IDExLjM4OTYgMjQuODMxNCAxMS44NzAyVjExLjg3MDJDMjQuODMxNCAxMi4zNTA3IDI0LjQ0MTggMTIuNzQwMyAyMy45NjEzIDEyLjc0MDNIMjAuNDgwNkMyMC4wMDAxIDEyLjc0MDMgMTkuNjEwNSAxMi4zNTA3IDE5LjYxMDUgMTEuODcwMlYxMS44NzAyWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yMS41NTU4IDI0LjI5NzJDMjEuNjk1NSAyNC4zMDk5IDIxLjg0MDggMjQuMzE2MiAyMS45OTE1IDI0LjMxNjJDMjIuNzc3OCAyNC4zMTYyIDIzLjQ2MTYgMjQuMTYyNCAyNC4wNDI4IDIzLjg4ODlDMjQuNTU1NiAyMy42MzI1IDI0Ljk4MjkgMjMuMjczNSAyNS4zMjQ4IDIyLjgyOTFWMjQuMDc2OUgyN1YxOC40MzU5QzI3IDE3LjM1OSAyNi43MjY1IDE2LjUzODUgMjYuMTk2NiAxNS45NzQ0QzI1LjU4MTIgMTUuMzI0OCAyNC42MjQgMTUgMjMuMzI0OCAxNUMyMi4yMzA4IDE1IDIxLjM0MTkgMTUuMTg4IDIwLjY5MjMgMTUuNTk4M0MyMC4yMTMzIDE1Ljg4MTMgMTkuODQ1MyAxNi4yNzUzIDE5LjU5MjUgMTYuNzcxNEwxOS44NTI0IDE3Ljc2NzZMMjEuMDY4NCAxNy44NzE4QzIxLjE3MSAxNy4zNzYxIDIxLjQyNzQgMTcuMDE3MSAyMS44Mzc2IDE2Ljc3NzhDMjIuMTc5NSAxNi41NzI2IDIyLjY0MSAxNi40NzAxIDIzLjIwNTIgMTYuNDcwMUMyNC41Mzg1IDE2LjQ3MDEgMjUuMjA1MiAxNy4wODU1IDI1LjIwNTIgMTguMzE2MlYxOC42NzUyTDIzLjIyMjIgMTguNzI2NUMyMS45ODk2IDE4Ljc1ODkgMjEuMDAzMSAxOC45OTE0IDIwLjI5MjEgMTkuNDUzMkwyMC44NjU2IDIxLjY1MTdDMjAuOTE1MSAyMC42MjI1IDIxLjczNTMgMjAuMDc1OSAyMy4zNDE5IDIwLjA0MjdMMjUuMjA1MiAxOS45OTE1VjIwLjUwNDNDMjUuMjA1MiAyMS4xODggMjQuOTE0NiAyMS43NjkyIDI0LjM2NzUgMjIuMjMwOEMyMy44MjA1IDIyLjY5MjMgMjMuMTcxIDIyLjkzMTYgMjIuNDAxNyAyMi45MzE2QzIxLjk0MDIgMjIuOTMxNiAyMS41NjQxIDIyLjgxMiAyMS4yOTA2IDIyLjU4OTdDMjEuMTkxNCAyMi41MTM5IDIxLjExMDIgMjIuNDMyMSAyMS4wNDYxIDIyLjM0MzZMMjEuNTU1OCAyNC4yOTcyWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNS4yMTc0IDI2TDE2IDI5TDI2IDI5QzI4LjIwOTIgMjkgMzAgMjcuMjA5MSAzMCAyNUwzMCAxMEMzMCA3Ljc5MDg2IDI4LjIwOTIgNiAyNiA2SDE2Ljc4MjZMMTcuMzA0NCA4TDI2IDhDMjcuMTA0NiA4IDI4IDguODk1NDMgMjggMTBMMjggMjVDMjggMjYuMTA0NiAyNy4xMDQ2IDI3IDI2IDI3TDE3LjU0NTIgMjdMMTcuMjg0MyAyNkgxNS4yMTc0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNC40NTQ4IDVINkM0Ljg5NTQzIDUgNCA1Ljg5NTQzIDQgN1YyMkM0IDIzLjEwNDYgNC44OTU0MyAyNCA2IDI0SDE5LjQxMTNMMTQuNDU0OCA1Wk0xNiAzSDZDMy43OTA4NiAzIDIgNC43OTA4NiAyIDdWMjJDMiAyNC4yMDkxIDMuNzkwODYgMjYgNiAyNkgyMkwxNiAzWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMS4wMTYzIDEwLjA5ODdDMTAuNzMyOCAxMC4xNjUyIDEwLjQyMjUgMTAuMDg3OSAxMC4yMDE1IDkuODY2ODlMOC4zNTU1OSA4LjAyMTAxQzguMDE1NzcgNy42ODExOSA4LjAxNTc3IDcuMTMwMjQgOC4zNTU1OSA2Ljc5MDQyQzguNjk1NDEgNi40NTA2MSA5LjI0NjM2IDYuNDUwNjEgOS41ODYxOCA2Ljc5MDQyTDEwLjgxNjggOC4wMjEwMkwxMi4wNDc0IDYuNzkwNDFDMTIuMzg3MiA2LjQ1MDYgMTIuOTM4MiA2LjQ1MDYgMTMuMjc4IDYuNzkwNDFDMTMuNjE3OCA3LjEzMDIzIDEzLjYxNzggNy42ODExOCAxMy4yNzggOC4wMjFMMTEuNDMyMSA5Ljg2Njg4QzExLjMxMzMgOS45ODU2OSAxMS4xNjg3IDEwLjA2MyAxMS4wMTYzIDEwLjA5ODdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTAuODc2OCAxMS45NzE5QzEyLjE3NTkgMTEuOTcxOSAxMy4xMzMyIDEyLjI5NjYgMTMuNzQ4NiAxMi45NDYyQzE0LjI3ODUgMTMuNTEwMyAxNC41NTIgMTQuMzMwOCAxNC41NTIgMTUuNDA3OFYyMS4wNDg4SDEyLjg3NjhWMTkuODAwOUMxMi41MzQ5IDIwLjI0NTQgMTIuMTA3NiAyMC42MDQzIDExLjU5NDcgMjAuODYwOEMxMS4wMTM1IDIxLjEzNDMgMTAuMzI5OCAyMS4yODgxIDkuNTQzNDUgMjEuMjg4MUM4LjYyMDM4IDIxLjI4ODEgNy45MDI0MyAyMS4wNDg4IDcuMzg5NjEgMjAuNTg3MkM2LjgyNTUxIDIwLjEyNTcgNi41NTIgMTkuNTI3NCA2LjU1MiAxOC43OTI0QzYuNTUyIDE3LjgwMDkgNi45NDUxNiAxNy4wMzE3IDcuNzMxNDkgMTYuNTAxOEM4LjQ0OTQ0IDE1Ljk4OSA5LjQ3NTA4IDE1LjczMjUgMTAuNzc0MiAxNS42OTg0TDEyLjc1NzEgMTUuNjQ3MVYxNS4yODgxQzEyLjc1NzEgMTQuMDU3MyAxMi4wOTA1IDEzLjQ0MTkgMTAuNzU3MSAxMy40NDE5QzEwLjE5MyAxMy40NDE5IDkuNzMxNDkgMTMuNTQ0NSA5LjM4OTYxIDEzLjc0OTZDOC45NzkzNSAxMy45ODkgOC43MjI5NCAxNC4zNDc5IDguNjIwMzggMTQuODQzN0w2LjgyNTUxIDE0LjY4OThDNy4wMTM1NCAxMy43MzI1IDcuNDkyMTcgMTMuMDE0NiA4LjI0NDMxIDEyLjU3MDJDOC44OTM4OCAxMi4xNTk5IDkuNzgyNzcgMTEuOTcxOSAxMC44NzY4IDExLjk3MTlaTTEyLjc1NzEgMTYuOTYzM0wxMC44OTM5IDE3LjAxNDZDOS4yMzU3NiAxNy4wNDg4IDguNDE1MjUgMTcuNjMgOC40MTUyNSAxOC43MjRDOC40MTUyNSAxOS4wNjU5IDguNTUyIDE5LjMzOTQgOC44NDI2IDE5LjU2MTZDOS4xMTYxIDE5Ljc4MzggOS40OTIxNyAxOS45MDM1IDkuOTUzNzEgMTkuOTAzNUMxMC43MjI5IDE5LjkwMzUgMTEuMzcyNSAxOS42NjQyIDExLjkxOTUgMTkuMjAyNkMxMi40NjY1IDE4Ljc0MTEgMTIuNzU3MSAxOC4xNTk5IDEyLjc1NzEgMTcuNDc2MVYxNi45NjMzWiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg==);
  233. }
  234. }
  235. }
  236. }
  237. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/91185
推荐阅读
  

闽ICP备14008679号