本帖最后由 极光creeper 于 2022-12-26 23:36 编辑


来晒一下过程和逻辑,还有一些todo的部分
不能直接给大家是因为,这个是可爱的yys2077特供版(重要原因),以及还有一些版务处理的部分(完全不重要的原因)
而且我用的还不是油猴

大概写了2个小时左右,有不完善的地方,海涵(这个算是笔记?)
今天正好也看到有人要写这种东西,有一些天坑要来说一下,主要集中在最后一页里





2021.12 数据,可能有更多内容


来晒一下过程和逻辑,还有一些todo的部分
不能直接给大家是因为,这个是可爱的yys2077特供版(重要原因),以及还有一些版务处理的部分(完全不重要的原因)
而且我用的还不是油猴


大概写了2个小时左右,有不完善的地方,海涵(这个算是笔记?)
今天正好也看到有人要写这种东西,有一些天坑要来说一下,主要集中在最后一页里








先来看第一个部分



界面实现比较简单:

1. 疯狂createElement() 加 appendChild(),设置className或id,以便接下来的css和元素获取

2. 写按钮toggle的显示逻辑,显示与不显示,很方便

3. 遍历,for,把表情格子腾出来 + 预览

4. 一些简单的css动画效果 + 处理一下border的融合

根据你的元素情况,可以用 负margin 或者 border-collapse 处理

5. 由于后文要在dz的编辑器内获取 range & selection 部分,

焦点必须保证在 textarea / iframe (dz的两套逻辑) 里

于是我给表情界面里的一些元素添加了 return false;

如果出现问题可能要再 e.preventDefault,不过我没试出来问题





随后就是翻页功能:

qq里的表情翻页是一次性翻3行(每页有3行的表情)

根据测量,qq内表情预览的大小大约是70*70

内部有个几px的白边padding,图片position是center,然后加contain适应

我再测量了一下,在表情外面套了一个div作为box,每次翻页移动的高度大概是 72*3 px

(实际情况请根据各位自己写的代码和css来测量,我只是说我的情况23333)


1. 开一个Number1来记录目前页码,再开一个Boolean来判断是用户在滚动还是代码导致的滚动

最后再开一个Number2存一下移动前的移动高度

2. 添加scroll监听器,由于是代码在帮用户滚,不需要节流/防抖

3. 储存的移动高度Number2 减去 element.scrollTop

大于0是向上滚,小于0是向下滚,不考虑0,因为这时候没有滚

4. 直接用 scrollTo() 滚,高度可以直接用页码+1或-1,乘以一页高度 (72*3) 来计算应该移动到哪里

5. scrollTo 会触发监听器,然后变成无限 scrollTo,所以在每次移动后要用一开始准备的Boolean救命

我这边是 1 代表下一次监听到的是代码滚动,滚动结束后异或一下,0 则是代表下一次是用户滚动


当然,我的逻辑是一次性直接到下一页,各位也可以再加一个简单的动画逻辑

直接 requestAnimationFrame(callback) 即可

比如每次移动的距离为 当前位置s 到 目标位置t 的差的绝对值 的1/4,这样速度是从快到慢,左高右低

而qq内的目测是 慢->快->慢 ,也就是有个峰,大家可以自己去实现一下,吾辈的水平有限,有一点臃肿


最后考虑一下浏览器的兼容情况

如果ie低版本完全不兼容的话还要自己写一个setTimeout自己手动callback

最后几个兼容情况直接 || 连起来就好







什么?不是已经写完了吗??

哦 原来极光使用了图床作为表情的储存地点

对于外链图床而言,这是非常普通的一件事,最多就是表情预览加载不出来而已


可如果使用mcbbs的附件作为图床,那么每次打开页面的时候就会请求很多次图片

这些图片可能会让你被403或者别的什么

虽然浏览器的缓存可以拯救你的账号,让你很少被403,可我们还需要一些更加优雅的解决方案


逻辑


使用canvas制作 小缩略图/劣化图,取base64,再存到 localStorage 里 :

1. 假装我们有个url,createElement('canvas');

调整画布大小到和图片一致

随后把图片塞进去 drawImage();

最后 canvas.toDataURL("image/png");

[!] 得到了 base64


如果不用论坛图床,可能要考虑跨域问题(?),似乎用不上

setAttribute("crossOrigin",'Anonymous');


2. 在得到base64之前加一段压缩逻辑,

计算 背景样式 contains 的自适应情况

判断图片长和宽哪个高

假设框70x70,边框留白5px,那么内部就是60px

取长宽最大的那个,变成60px,另一条边同比例缩小即可

获得了变化后的高度,然后绘图drawImage,最后toDataURL


此外还有一个jpeg的劣化方案,与上面类似


3. 存起来

localStorage.setItem( 'emoji--' + i , base64);


由于劣化/缩略图,预览图非常非常小,不太需要在意占用的空间

不过这个只是逻辑,我觉得写的时候会有一点其他的问题

而且必须要求浏览器支持localStorage




此外,我们还要再开个数组存是否有缩略图

并且准备一套 '用户更新表情' 的逻辑,不过会比较麻烦

我没写。








剩下的就是最烦的部分了,也就是往编辑器里写入东西

我觉得比较正常优雅的方法是:

使用appendChild的方法往页面中写入script,直接使用dz自带的editor.js的函数

找个办法写callback/hook,然后就可以自己注册一些富文本编辑器内的功能了

我们就可以用自带的函数往编辑器里插入图片

可以一次性支持textarea和iframe(dz自带的两套编辑器,前者是纯文本模式,后者是非纯文本模式)


然而我的浏览器插件默认是单独插了个script进去,虽然这完全不影响我用上面的手段


我在写的时候其实是知道可以直接append的,但是必须先去读懂dz那个editor.js里的逻辑

我打开那个js一看:

惊人的变量命名 wysiwyg,真就所见即所得呗

不明觉厉的元素id_fullswitcher,_adv_s3

不想看,吐了,自己手动加吧


随后我发现了一个很离谱的东西

dz的编辑器高级模式有 纯文本模式 和 非纯文本模式

纯文本模式是 textarea (在iframe外)

非纯文本模式是 iframe (里面有另一层html)

当你点 input 的时候,它俩里的内容会互相转换


插入文本的逻辑和方案很朴素,靠range和selection得到光标的地点,然后在后面加入内容


操作 textarea 里的内容是富文本编辑器的基操

可是在外面动iframe里的东西就有点鬼畜了,超出了我能力范围

再加上是在 iframe>body 里而非 iframe>input/textarea 里,我假期里再研究下怎么插


反正现在iframe里的range和selection我拿不到

百度了一下,解决方法似乎是有的,但是我没太看懂

我看最近论坛有些脚本开发想写这个,特此过来提醒一下各位注意这个天坑

因此,我现在写的功能只有在纯文本模式下才能使用


对于ie:

有一套不符合w3c规范的方法

直接获取 theSelection = document.selection;

随后 theSelection.createRange().text = '[ img]url[/img]';

简单简单


对于非ie:

有一套 eStart = element.selectionStart; 和 eEnd = element.selectionEnd;

分别是选中区域的开始和结尾的位置

我们要的效果是,如果有选区,替换选区的内容,否则就光标后插入


到了喜闻乐见的拼串环节

element.value.substr(0,eStart) + '[ img]url[/img]' + element.value.substr(eEnd, element.value.length);

接下来,我们要移动光标到最后的部分

假装 strLength = '[ img]url[/img]'.length;

而最后的部分其实是一个光标而非一块区域,也就是说eStart应该等于eEnd

element.selectionStart = eStart + strLength;

element.selectionEnd = eStart + strLength;

[不要写连等]

不知道类型怎么样,我没开strict模式,这样写倒是能过。


todo

应该是iframe/iframe下body的原因,导致我没办法直接操作非纯文本的内容

不知道各位坛友是否有什么经验

不过大概知道了原因,我假期里可以再试着写写看