diff --git a/2.html b/2.html new file mode 100644 index 0000000..d02d898 --- /dev/null +++ b/2.html @@ -0,0 +1,159 @@ + + + + + + 可点击其他元素+保持光标不丢 + + + +
+ +
+ 点击这里输入...点击下方按钮/链接/区域,它们能正常工作,光标也不会消失 +
+ + + + + + + +
+ + + + diff --git a/css/index.css b/css/index.css index e1754c4..e929e07 100644 --- a/css/index.css +++ b/css/index.css @@ -48,8 +48,7 @@ editor { .container { min-height: 100vh; background-color: #f5f5f5; - padding-bottom: 3.2rem; - padding-bottom: 8.6667rem; + padding-bottom: 300px; padding-top: 0.4rem; } .container .title-box { @@ -81,7 +80,7 @@ editor { border-radius: 0.4rem; } .container .editor-box .editor { - margin: 0.4rem; + margin: 0.4rem 0.4rem 0.2rem; width: 8.6rem; min-height: 10rem; border: none; @@ -121,7 +120,24 @@ editor { height: 1px; display: inline-block; } -.container .editor-box .label { +.container .editor-box .operate-box { + display: flex; + flex-direction: column; + padding-top: 0.2rem; + transition: all 0.3s ease-in-out; +} +.container .editor-box .operate-box.fixed { + position: fixed; + bottom: 0; + left: 0; + width: 100vw; + background-color: #fff; + z-index: 999; +} +.container .editor-box .operate-box.fixed .label { + width: 100vw; +} +.container .editor-box .operate-box .label { white-space: nowrap; /* height: 0.84rem; */ margin-bottom: 0.2rem; @@ -129,7 +145,7 @@ editor { overflow: auto; padding-bottom: 0.2rem; } -.container .editor-box .label .item { +.container .editor-box .operate-box .label .item { width: fit-content; height: 0.64rem; line-height: 0.64rem; @@ -140,19 +156,19 @@ editor { padding: 0 0.24rem; display: inline-flex; } -.container .editor-box .label .item:not(:last-of-type) { +.container .editor-box .operate-box .label .item:not(:last-of-type) { margin-right: 0.2rem; } -.container .editor-box .label .item:first-of-type { +.container .editor-box .operate-box .label .item:first-of-type { margin-left: 0.4rem; } -.container .editor-box .label .item:last-of-type { +.container .editor-box .operate-box .label .item:last-of-type { margin-right: 0.4rem; } -.container .editor-box .btn-list { +.container .editor-box .operate-box .btn-list { margin: 0 0.4rem 0.4rem; } -.container .editor-box .btn-list .item { +.container .editor-box .operate-box .btn-list .item { width: fit-content; height: 0.72rem; line-height: 0.72rem; @@ -166,15 +182,15 @@ editor { margin-right: 0.2rem; position: relative; } -.container .editor-box .btn-list .item.pitch { +.container .editor-box .operate-box .btn-list .item.pitch { background-color: #f6f6bd; } -.container .editor-box .btn-list .item .icon { +.container .editor-box .operate-box .btn-list .item .icon { width: 0.4rem; height: 0.4rem; margin-right: 0.14rem; } -.container .editor-box .btn-list .item .file { +.container .editor-box .operate-box .btn-list .item .file { opacity: 0; /* 隐藏输入框 */ background: transparent; @@ -186,7 +202,7 @@ editor { height: 100%; cursor: pointer; } -.container .editor-box .btn-list .item .file::after { +.container .editor-box .operate-box .btn-list .item .file::after { content: ""; width: 100%; height: 100%; @@ -194,14 +210,14 @@ editor { top: 0; left: 0; } -.container .editor-box .btn-list .unfold { +.container .editor-box .operate-box .btn-list .unfold { width: 0.72rem; height: 0.72rem; background-color: #f6f6f6; border: 0.0133rem solid #ebebeb; border-radius: 1.4667rem; } -.container .editor-box .btn-list .unfold .icon { +.container .editor-box .operate-box .btn-list .unfold .icon { width: 0.32rem; height: 0.32rem; } @@ -292,7 +308,7 @@ editor { height: 0; } 100% { - height: 8.6667rem; + height: 300px; } } .container .pop .emoji-system-list .item { diff --git a/css/index.less b/css/index.less index 0a2b46c..0e02045 100644 --- a/css/index.less +++ b/css/index.less @@ -57,8 +57,8 @@ editor { .container { min-height: 100vh; background-color: rgba(245, 245, 245, 1); - padding-bottom: 3.2rem; - padding-bottom: 8.6667rem; + padding-bottom: 300px; + // padding-bottom: 8.6667rem; padding-top: 0.4rem; @@ -93,7 +93,7 @@ editor { border-radius: 0.4rem; .editor { - margin: 0.4rem; + margin: 0.4rem 0.4rem 0.2rem; width: 8.6rem; min-height: 10rem; border: none; @@ -146,98 +146,117 @@ editor { } } - .label { - white-space: nowrap; - /* height: 0.84rem; */ - margin-bottom: 0.2rem; - width: 9.4rem; - overflow: auto; - padding-bottom: 0.2rem; + .operate-box { + display: flex; + flex-direction: column; + padding-top: 0.2rem; + transition: all 0.3s ease-in-out; - .item { - width: fit-content; - height: 0.64rem; - line-height: 0.64rem; - background-color: rgba(246, 246, 246, 1); - border-radius: 1.46rem; - font-size: 0.28rem; - color: #606060; - padding: 0 0.24rem; - display: inline-flex; - - &:not(:last-of-type) { - margin-right: 0.2rem; - } - - &:first-of-type { - margin-left: 0.4rem; - } - - &:last-of-type { - margin-right: 0.4rem; + &.fixed { + position: fixed; + bottom: 0; + left: 0; + width: 100vw; + background-color: #fff; + z-index: 999; + .label { + width: 100vw; } } - } - .btn-list { - margin: 0 0.4rem 0.4rem; + .label { + white-space: nowrap; + /* height: 0.84rem; */ + margin-bottom: 0.2rem; + width: 9.4rem; + overflow: auto; + padding-bottom: 0.2rem; - .item { - width: fit-content; - height: 0.72rem; - line-height: 0.72rem; - background-color: rgba(246, 246, 246, 1); - border: 0.0133rem solid rgba(235, 235, 235, 1); - border-radius: 1.46rem; - font-family: "PingFangSC-Regular", "PingFang SC", sans-serif; - font-size: 0.32rem; - color: #555555; - padding: 0 0.24rem; - margin-right: 0.2rem; - position: relative; + .item { + width: fit-content; + height: 0.64rem; + line-height: 0.64rem; + background-color: rgba(246, 246, 246, 1); + border-radius: 1.46rem; + font-size: 0.28rem; + color: #606060; + padding: 0 0.24rem; + display: inline-flex; - &.pitch { - background-color: rgba(246, 246, 189, 1); - } + &:not(:last-of-type) { + margin-right: 0.2rem; + } - .icon { - width: 0.4rem; - height: 0.4rem; - margin-right: 0.14rem; - } + &:first-of-type { + margin-left: 0.4rem; + } - .file { - opacity: 0; /* 隐藏输入框 */ - background: transparent; - border: none; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - cursor: pointer; - - &::after { - content: ""; - width: 100%; - height: 100%; - position: absolute; - top: 0; - left: 0; + &:last-of-type { + margin-right: 0.4rem; } } } - .unfold { - width: 0.72rem; - height: 0.72rem; - background-color: rgba(246, 246, 246, 1); - border: 0.0133rem solid rgba(235, 235, 235, 1); - border-radius: 1.4667rem; + .btn-list { + margin: 0 0.4rem 0.4rem; - .icon { - width: 0.32rem; - height: 0.32rem; + .item { + width: fit-content; + height: 0.72rem; + line-height: 0.72rem; + background-color: rgba(246, 246, 246, 1); + border: 0.0133rem solid rgba(235, 235, 235, 1); + border-radius: 1.46rem; + font-family: "PingFangSC-Regular", "PingFang SC", sans-serif; + font-size: 0.32rem; + color: #555555; + padding: 0 0.24rem; + margin-right: 0.2rem; + position: relative; + + &.pitch { + background-color: rgba(246, 246, 189, 1); + } + + .icon { + width: 0.4rem; + height: 0.4rem; + margin-right: 0.14rem; + } + + .file { + opacity: 0; /* 隐藏输入框 */ + background: transparent; + border: none; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + cursor: pointer; + + &::after { + content: ""; + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + } + } + } + + .unfold { + width: 0.72rem; + height: 0.72rem; + background-color: rgba(246, 246, 246, 1); + border: 0.0133rem solid rgba(235, 235, 235, 1); + border-radius: 1.4667rem; + + .icon { + width: 0.32rem; + height: 0.32rem; + } } } } @@ -341,7 +360,7 @@ editor { } 100% { - height: 8.6667rem; + height: 300px; } } diff --git a/index.html b/index.html index 04a35f7..95f9ea4 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ - +
@@ -16,33 +16,33 @@
-
- 森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅vv -
+
-
-
#推荐标签{{index}}
-
+
+
+
#推荐标签{{ index }}
+
-
-
- -
段落标题
-
-
- -
图片
- -
-
- -
表情
-
-
- +
@@ -52,15 +52,15 @@
匿名发布
- +
@@ -70,12 +70,12 @@
- + diff --git a/js/index.js b/js/index.js index ec6531e..d011fc8 100644 --- a/js/index.js +++ b/js/index.js @@ -10,6 +10,7 @@ createApp({ let info = ref({ anonymity: 0, + content: "森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅森岛帆高收到过电饭锅电饭锅vv", }); const titleTextarea = ref(null); @@ -33,6 +34,8 @@ createApp({ document.addEventListener("selectionchange", getFocusedNodeName); // 添加键盘事件监听 document.addEventListener("keydown", handleDeleteKey); + + judgeIsEmpty(); }); onUnmounted(() => { @@ -259,6 +262,8 @@ createApp({ // 将范围添加到选择对象,不设置焦点 selection.addRange(range); + + editorRef.value.blur(); }; let isEmpty = ref(true); @@ -273,47 +278,52 @@ createApp({ } judgeIsEmpty(); + + getCursorPosition("text"); + }; + + const fixedState = ref(false); + const onEditorFocus = () => { + setTimeout(() => getKeyboardHeight(), 500); + }; + + const onEditorBlur = () => { + // fixedState.value = false; }; // 判断是否为空 const judgeIsEmpty = () => { const text = editorRef.value.innerText; - console.log("text", text); - isEmpty.value = text.length == 0 && !editorRef.value.querySelector("img"); }; const paragraphTitle = () => { // 保存当前滚动位置 const scrollTop = window.scrollY; - + editorRef.value.focus(); if (!lastSelection) return; const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(lastSelection); - + // 使用try-catch确保即使命令执行失败也能恢复滚动位置 try { document.execCommand("formatBlock", false, isPTitle.value ? "P" : "H2"); } catch (error) { console.error("应用段落格式失败:", error); } - + // 立即恢复滚动位置 window.scrollTo(0, scrollTop); - + // 更新状态 - updatePTitleStatus(); + setTimeout(() => updatePTitleStatus(), 100); }; const updatePTitleStatus = () => { if (lastSelection) { - const node = lastSelection.commonAncestorContainer; - let parentElement = node.parentElement; - - isPTitle.value = node.nodeName === "H2" || (node.nodeType === Node.TEXT_NODE && node.parentNode?.nodeName === "H2"); - + let parentElement = lastSelection.commonAncestorContainer; // 死循环,直到遇到终止条件 while (true) { // 如果没有父元素了(到达文档根节点),退出循环返回false @@ -402,10 +412,11 @@ createApp({ lastSelection.setEndAfter(textNode); judgeIsEmpty(); - getCursorPosition(); + getCursorPosition("emoji"); }; - const getCursorPosition = () => { + // 将当前输入位置滚动到可视区域 + const getCursorPosition = (type = "emoji") => { const range = lastSelection; const tempElement = document.createElement("span"); tempElement.classList.add("cursor"); @@ -420,13 +431,18 @@ createApp({ // 计算目标位置:中间偏上(视口高度的30%位置) // 公式:元素顶部相对于视口的位置 + 滚动距离 - 目标位置(视口高度的30%) - const targetPosition = window.scrollY + rect.top - viewportHeight * 0.3; // 30%位置,比正中间更靠上 + // const targetPosition = window.scrollY + rect.top - viewportHeight * 0.3; // 30%位置,比正中间更靠上 + const height = type == "emoji" ? 300 : keyboardHeight; + const targetPosition = window.scrollY + rect.top - (originalWindowHeight - height) + 40; + console.log("originalWindowHeight", originalWindowHeight, "targetPosition", targetPosition, "window.scrollY", window.scrollY, "targetPosition"); // 平滑滚动到目标位置 - window.scrollTo({ - top: targetPosition, - behavior: "smooth", // 平滑滚动,移除则为瞬间滚动 - }); + if (Math.abs(targetPosition - window.scrollY) > 10) { + window.scrollTo({ + top: targetPosition, + behavior: "smooth", // 平滑滚动,移除则为瞬间滚动 + }); + } // 移除临时元素 tempElement.parentNode.removeChild(tempElement); @@ -434,6 +450,25 @@ createApp({ const cutAnonymity = () => (info.value.anonymity = info.value.anonymity ? 0 : 1); - return { cutAnonymity, isEmpty, selectEmoji, closeEmoji, openEmoji, optionEmoji, emojiState, insertLabel, editorRef, info, title, titleLength, titleTextarea, adjustTextareaHeight, isPTitle, paragraphTitle, insertImage, onEditorInput }; + const operateRef = ref(null); + + onMounted(() => { + // 初始化时记录初始窗口高度 + originalWindowHeight = window.visualViewport.height; + keyboardHeight = originalWindowHeight / 2; + }); + + let originalWindowHeight = 0; + let keyboardHeight = 0; + + // 获取键盘高度 + const getKeyboardHeight = () => { + const currentHeight = window.visualViewport.height; + + // 键盘弹出时,窗口高度会减小 + if (currentHeight < originalWindowHeight) keyboardHeight = originalWindowHeight - currentHeight; + }; + + return { operateRef, fixedState, onEditorBlur, onEditorFocus, cutAnonymity, isEmpty, selectEmoji, closeEmoji, openEmoji, optionEmoji, emojiState, insertLabel, editorRef, info, title, titleLength, titleTextarea, adjustTextareaHeight, isPTitle, paragraphTitle, insertImage, onEditorInput }; }, }).mount("#appIndex");