react.js 原生文字下划线标注功能开发
生活随笔
收集整理的這篇文章主要介紹了
react.js 原生文字下划线标注功能开发
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
react.js 原生文字下劃線標注功能開發 (原生js封裝)
github地址
效果展示:
可以輸出標注的內容👆
index.jsx
import React, { useState, useEffect, useRef, } from 'react'; import { Menu, Button, Select, Tag } from 'antd'; import { CheckOutlined, RollbackOutlined ,UndoOutlined } from '@ant-design/icons'; import { LabelText } from './labeltext'; import './index.css'; const str ='WebGL(全寫Web Graphics Library)是一種3D繪圖協議,這種繪圖技術標準允許把JavaScript和OpenGL ES 2.0結合在一起,通過增加OpenGL ES 2.0的一個JavaScript綁定,WebGL可以為HTML5 Canvas提供硬件3D加速渲染,這樣Web開發人員就可以借助系統顯卡來在瀏覽器里更流暢地展示3D場景和模型了,還能創建復雜的導航和數據視覺化。顯然,WebGL技術標準免去了開發網頁專用渲染插件的麻煩,可被用于創建具有復雜3D結構的網站頁面,甚至可以用來設計3D網頁游戲等等。還有一些廠商對MSAA技術進行了擴展,這里只簡單提下: - 2006年,NVIDIA提出的CSAA(coverage sampling antialiasing),AMD提出的EQAA(enhanced quality antialiasing),嘗試優化MSAA的Coverage來改進AA效果,但是提升的效果非常有限,參見[7, 8]的介紹; - 2007年,ATI提出的CFAA(custom filter antialiasing),嘗試優化MSAA的Resolve階段的過濾算法(默認是Box Filter)來改進AA效果;移動平臺的TBR架構能高效的支持MSAA,但是由于MSAA的原理限制,它不適用于延遲管線(deferred pipeline)。此時,快速近似抗鋸齒(FXAA,fast approximate antialising)可以作為MSAA算法的補充。FXAA是通過單一次、全屏的后處理來實現的,也是對邊緣像素多次超采樣,達到邊緣鋸齒的效果,是一個偏經驗性的算法(簡單說,就是別問我它為什么有效果),虛幻4引擎集成了該技術[9]。光珊化后,每一個像素點都包含了 顏色 、深度 、紋理數據, 這個我們叫做片元小tips : 每個像素的顏色由片元著色器的gl_FragColor提供接收光柵化階段生成的片元,在光柵化階段中,已經計算出每個片元的顏色信息,這一階段會將片元做逐片元挑選的操作,處理過的片元會繼續向后面的階段傳遞。 片元著色器運行的次數由圖形有多少個片元決定的。逐片元挑選通過模板測試和深度測試來確定片元是否要顯示,測試過程中會丟棄掉部分無用的片元內容,然后生成可繪制的二維圖像繪制并顯示。● 深度測試: 就是對 z 軸的值做測試,值比較小的片元內容會覆蓋值比較大的。(類似于近處的物體會遮擋遠處物體)。● 模板測試: 模擬觀察者的觀察行為,可以接為鏡像觀察。標記所有鏡像中出現的片元,最后只繪制有標記的內容。'; let labelText; export default function TextMark() {const contextRef = useRef(null);const [modalSelectContent, setModalSelectContent] = useState(null);window.setModalSelectContent = setModalSelectContent;const items = [{label: <Tag color="#c41d7f">#c41d7f</Tag>,key: '#c41d7f',color: '#c41d7f',background: '#fff0f6',border: '#ffadd2',},{label: <Tag color="#cf1322">#cf1322</Tag>,key: '#cf1322',color: '#cf1322',background: '#fff1f0',border: '#ffa39e',},{label: <Tag color="#d4380d">#d4380d</Tag>,key: '#d4380d',color: '#d4380d',background: '#fff2e8',border: '#ffbb96',},{label: <Tag color="#d46b08">#d46b08</Tag>,key: '#d46b08',color: '#d46b08',background: '#fff7e6',border: '#ffd591',},{label: <Tag color="#d48806">#d48806</Tag>,key: '#d48806',color: '#d48806',background: '#fffbe6',border: '#ffe58f',},{label: <Tag color="#7cb305">#7cb305</Tag>,key: '#7cb305',color: '#7cb305',background: '#fcffe6',border: '#eaff8f',},{label: <Tag color="#389e0d">#389e0d</Tag>,key: '#389e0d',color: '#389e0d',background: '#f6ffed',border: '#b7eb8f',},{label: <Tag color="#08979c">#08979c</Tag>,key: '#08979c',color: '#08979c',background: '#e6fffb',border: '#87e8de',},{label: <Tag color="#0958d9">#0958d9</Tag>,key: '#0958d9',color: '#0958d9',background: '#e6f4ff',border: '#91caff',},{label: <Tag color="#1d39c4">#1d39c4</Tag>,key: '#1d39c4',color: '#1d39c4',background: '#f0f5ff',border: '#adc6ff',},{label: <Tag color="#531dab">#531dab</Tag>,key: '#531dab',color: '#531dab',background: '#f9f0ff',border: '#d3adf7',},];const [menuKey, setMenuKey] = useState(Math.random());// 自定義tagconst tagRender = (props) => {const { label, value } = props;return (<>{label ? (<Tag color={value} style={{ marginLeft: 5 }}>{label}</Tag>) : null}</>);};useEffect(() => {// Step1 :// eslint-disable-next-line react-hooks/exhaustive-depslabelText = new LabelText({el: contextRef.current,});// Step2 : 模擬接口調用setTimeout(() => {labelText.addText(str);}, 0);}, []);return (<><header><div className="operation"><Buttontype="primary"shape="round"icon={<RollbackOutlined />}onClick={() => {window.repeal();}}>上一步</Button><Buttontype="primary"shape="round"dangericon={<UndoOutlined />}onClick={() => {window.clean();}}>重置標注</Button><Buttontype="primary"shape="round"icon={<CheckOutlined />}onClick={() => {console.log(labelText.output());labelText.output();}}>標注結果</Button><div className="labelSelect"></div></div></header><div className="container"><divclassName="content-container interval"ref={contextRef}></div>{/* 下拉選擇 */}<div id="select-modal"><div className="input-select"><Selectsize="large"mode="multiple"allowClear={modalSelectContent}defaultOpen={false}showArrow={false}dropdownStyle={{ height: 0 }}showSearch={false}placeholder="請選擇標簽"maxTagCount={1}value={modalSelectContent || null}tagRender={tagRender}onClear={() => {labelText.deleteLabel(modalSelectContent);}}style={{ width: '100%' }}/></div><div className="label-show"><Menukey={ menuKey }items={items}onSelect={(item) => {window.selectLabel(item?.key);setMenuKey(Math.random());}}/></div></div><div className="operation-area"><div className="card-title">操作</div></div></div></>); }index.css
.operation {padding: 0px 30px;box-sizing: border-box;line-height: 50px;background-color: #ffffff;box-shadow: 2px 0 6px rgb(0 21 41 / 35%); }.ant-btn {margin-right: 5px; }.labelSelect {margin-bottom: 10px;line-height: 30px; }.container {width: 100%;height: calc(100% - 90px);display: flex;flex-direction: row;justify-content: space-between;padding: 5px 10px; }.operation-area::-webkit-scrollbar, #canvas::-webkit-scrollbar {/*滾動條整體樣式*/width: 3px;/*高寬分別對應橫豎滾動條的尺寸*/height: 3px; }.operation-area::-webkit-scrollbar-thumb, #canvas::-webkit-scrollbar-thumb {/*滾動條里面小方塊*/border-radius: 5px;-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);background: rgba(0, 0, 0, 0.2); }.operation-area::-webkit-scrollbar-track, #canvas::-webkit-scrollbar-track {/*滾動條里面軌道*/-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);border-radius: 0;background: rgba(0, 0, 0, 0.1); }.operation-area {position: relative;padding: 0 5px;min-width: 200px;height: auto;margin-left: 5px;overflow-y: auto;box-shadow: 2px 0 6px rgb(0 21 41 / 35%); }.card-title {align-items: center;display: flex;flex-wrap: wrap;padding: 5px 8px;font-size: 1.25rem;font-weight: 800;letter-spacing: 0.0125em;line-height: 2rem;word-break: break-all; }.card-title::after {content: '';display: block;width: 100%;margin: 5px 0;height: 1px;border-bottom: 1px solid rgba(0, 0, 0, 0.2); }.radio-label {list-style: none;margin: 0;padding: 0;box-sizing: border-box; }.li-radio-content {padding: 10px;display: flex;flex-direction: row;justify-content: space-between;align-items: center;background-color: #ffffff;cursor: pointer; }.opacity {opacity: 0.3; }.li-radio-content:hover {background-color: rgb(245, 245, 245); }.li-radio {padding: 4px 12px;font-size: 12px;background-color: rgb(0, 156, 224);color: #ffffff;letter-spacing: 3px;border-radius: 15px; }.tjtyanjing, .tjtbiyan {margin-right: 15px; }.tjtlajitong1 {color: rgb(100, 100, 100); }.radio-select {background-color: #e0e0e0 !important; }/* 上面不太重要 */.content-container {flex: 1; }.text-selected {position: relative; }.text-selected::after {content: attr(data-attr);display: inline-block;position: absolute;top: 0px;left: 0px;font-size: 16px;width: auto;white-space: nowrap;height: 50px;color: attr(data-attr) !important; }.interval {padding: 0 20px;line-height: 70px;font-size: 16px; }.text-selected {position: relative; }#select-modal {display: none;width: 250px;padding: 10px;box-sizing: border-box;position: absolute;overflow-y: auto;overflow-x: hidden;contain: content;z-index: 9999;background-color: #ffffff;box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);border-radius: 4px; }.label-show {margin-top: 5px; }.next-select .next-select-inner {border: none;border-bottom: 1px solid #cfd2db;margin-bottom: 10px; }.ant-menu-light {border: 1px solid #cfd2db !important;border-radius: 4px;overflow: hidden;border-radius: 4px; }.ant-menu-vertical .ant-menu-item {margin-top: 0 !important;padding-left: 5px !important;margin-bottom: 0 !important; }.ant-menu-item {padding: 0 !important; }.ant-menu-light .ant-menu-item:hover{background-color: #e6f7ff;}labelText.js
/* eslint-disable no-unused-expressions */ /* eslint-disable no-loop-func */// todo: 切換顏色 export const LabelText = (function () {let _self;let _el;let initText;let mouseDataIndex = 0;let fixedTextStackLen = 0;let deleteRecord = [];function LabelText(opt) {_el = opt.el;_self = this;this.config = Object.assign({}, this.default, opt);this.color = 'tan';this.textStack = [];this.letters = []; // 存放被選中的文字內容selectText();}LabelText.prototype = {addText(htmlStr) {// 清空已有的標記內容clean();_el.innerHTML = htmlStr;initText = htmlStr;},output() {if (window._self.letters.length) {_self = window._self;}return _self.letters;},// 刪除選中deleteLabel(value) {if (window._self.letters.length && window._self.textStack.length) {_self = window._self;const deleteIndex = _self.letters.findIndex((item) => item.dataIndex === mouseDataIndex);let deleteSpanValue = 0;_el.style.cssText = `pointer-events: inherit;`;const deleteValue = _self.letters[deleteIndex];_el.innerHTML = _self.textStack.at(-1);for (let i = 0;i < _el.children.length === 1 ? 2 : _el.children.length;i++) {if (_el.children[i].getAttribute('data-index') ===mouseDataIndex) {deleteSpanValue = i;deleteRecord.push({idx: deleteSpanValue,deleteStr: deleteValue.str,prevMouseDataIndex: mouseDataIndex,});break;}}// 徹底清除記錄for (let o = 0; o < deleteRecord.length; o++) {const elChildrenIndex = Array.from(_el.children).findIndex((item) =>item.getAttribute('data-index') ===deleteRecord[o].prevMouseDataIndex);if (elChildrenIndex > -1) {const outerText =_el.children[elChildrenIndex].outerText;_el.children[elChildrenIndex].replaceWith(outerText);}}_self.letters.splice(deleteIndex, 1);_self.textStack.splice(deleteIndex, 1);document.getElementById('select-modal').style.display = 'none';mouseDataIndex = 0;window.setModalSelectContent(null);}},};// 改變顏色function changeColor() {const colors = document.getElementsByClassName('item-color');const tc = document.getElementsByClassName('lt-tool-colors')[0];for (let i = 0; i < colors.length; i++) {colors[i].onclick = () => {_self.color = this.dataset.color;console.log(tc);tc.style.backgroundColor = _self.color;};}}function selectText() {const selObj = window.getSelection();const range = document.createRange();_el.onmouseup = function (e) {if (e.target.classList.contains('text-selected')) {console.log('在相同位置劃線');mouseDataIndex = e.target.getAttribute('data-index');openModal(e, e.target.getAttribute('data-attr'));return;}const acrossText = selObj.toString();if (selObj.anchorNode === selObj.focusNode &&selObj.anchorOffset !== selObj.focusOffset) {if (window._self) {_self = window._self;}_el.style.cssText = `pointer-events: none;`;fixedTextStackLen++;const span = createSpan(fixedTextStackLen);// 從前往后if (selObj.anchorOffset < selObj.focusOffset) {range.setStart(selObj.anchorNode, selObj.anchorOffset);range.setEnd(selObj.anchorNode, selObj.focusOffset);} else {// 從后往前range.setStart(selObj.anchorNode, selObj.focusOffset);range.setEnd(selObj.anchorNode, selObj.anchorOffset);}range.surroundContents(span);// 確定 startIndex / endIndex (★ 核心)const maxDataIndex = [];let startIndex = 0;let endIndex = 0;for (let i = 0;i <Array.from(document.getElementsByClassName('content-container')[0].childNodes).length;i++) {const element =document.getElementsByClassName('content-container')[0].childNodes[i];if (element?.innerText) {maxDataIndex.push(element.getAttribute('data-index'));if (span.getAttribute('data-index') ===maxDataIndex.reduce((a, b) => (a > b ? a : b)[0])) {const index = i || 1;for (let k = 0; k < index; k++) {startIndex +=document.getElementsByClassName('content-container')[0].childNodes[k].length ||document.getElementsByClassName('content-container')[0].childNodes[k].innerText?.length ||0;endIndex +=document.getElementsByClassName('content-container')[0].childNodes[k].length ||document.getElementsByClassName('content-container')[0].childNodes[k].innerText?.length ||0;}endIndex +=document.getElementsByClassName('content-container')[0].childNodes[index].length ||document.getElementsByClassName('content-container')[0].childNodes[index].innerText.length - 1;break;}}}_self.letters.push({str: acrossText,tag: null,dataIndex: span.getAttribute('data-index'),startIndex,endIndex,});_self.textStack.push(_el.innerHTML);openModal(e);}};}// 打開彈窗function openModal(e, selectValue) {if (selectValue) {window.setModalSelectContent([selectValue]);}document.getElementById('select-modal').style.cssText = `display:block`;document.getElementById('select-modal').style.cssText = `display:block;top: ${e.offsetY + 120}px;left: ${e.offsetX}px`;}// 回退function repeal() {if (_self.textStack.length !== window._self.textStack.length) {_self = window._self;}if (!_self.textStack.length) {return;}if (_self.textStack.length === 1) {_el.innerHTML = initText;_self.letters = [];_self.textStack = [];_el.style.cssText = `pointer-events: inherit;`;document.getElementById('select-modal').style.display = 'none';return;}_self.letters.pop();_self.textStack.pop();_el.style.cssText = `pointer-events: inherit;`;_el.innerHTML = _self.textStack[_self.textStack.length - 1];mouseDataIndex = 0;document.getElementById('select-modal').style.display = 'none';window._self = _self;}window.repeal = repeal;// 全清function clean() {_el.innerHTML = initText;_self.textStack = [];_self.letters = [];deleteRecord = [];initText;mouseDataIndex = 0;fixedTextStackLen = 0;_el.style.cssText = `pointer-events: inherit;`;document.getElementById('select-modal').style.display = 'none';}window.clean = clean;// 創建spanfunction createSpan(index) {const spanEle = document.createElement('span');spanEle.className = 'text-selected';spanEle.setAttribute('data-index', index);spanEle.style.backgroundColor = _self.color;return spanEle;}function selectLabel(v) {const ts = document.getElementsByClassName('text-selected');// 修改路線if (mouseDataIndex) {console.log('修改路線');_self = window._self;const changeIndex = Array.from(ts).findIndex((item) =>item.getAttribute('data-index') === String(mouseDataIndex));ts[changeIndex].setAttribute('data-attr', v);const lettersAndTextStacksIndex = _self.letters.findIndex((item) => item.dataIndex === mouseDataIndex);_self.letters[lettersAndTextStacksIndex].tag = v;_self.textStack[lettersAndTextStacksIndex] = _el.innerHTML;_el.style.cssText = `pointer-events: inherit;`;mouseDataIndex = document.getElementById('select-modal').style.display = 'none';window.setModalSelectContent(null);mouseDataIndex = 0;window._self = _self;return;}// 修改select內容if (window?._self &&window?._self?.letters?.length !== _self?.letters?.length) {_self = {..._self,letters: [...window._self.letters, ..._self.letters],textStack: [...window._self.textStack, ..._self.textStack],};mouseDataIndex = 0;}// 正常添加const addIndex = Array.from(ts).findIndex((item) =>item.getAttribute('data-index') === String(fixedTextStackLen));ts[addIndex].setAttribute('data-attr', v);_self.letters[ts.length - 1 > 0 ? ts.length - 1 : 0].tag = v;_self.textStack[ts.length - 1 > 0 ? ts.length - 1 : 0] = _el.innerHTML;_el.style.cssText = `pointer-events: inherit;`;document.getElementById('select-modal').style.display = 'none';window.setModalSelectContent(null);mouseDataIndex = 0;window._self = _self;}window.selectLabel = selectLabel;return LabelText; })();http://localhost:3000/text-mark
**具體可以參考引用里的地址鏈接 **
總結
以上是生活随笔為你收集整理的react.js 原生文字下划线标注功能开发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL Server 负载均衡集群方案之
- 下一篇: 谷歌开发者版和beta版_Google工