目錄結(jié)構(gòu)和說明
目錄初始結(jié)構(gòu)
創(chuàng)建一些目錄以及文件燎含,如圖1-1 所示。這里只是隨手創(chuàng)的,也可以自定目錄結(jié)構(gòu)。
目錄說明
-
serval/script/harusame-dom.js
提供了創(chuàng)建 DOM 的工具皆看,比createElement(tagName)
那種要稍微方便一點,創(chuàng)建方式借鑒了虛擬DOM背零。使用方式就在這里說明了腰吟,之后也不細(xì)說。
// 創(chuàng)建元素節(jié)點
var $node = SatoriDom.compile(e('div', {'class': 'demo', 'id': 'demo'}, '這里是文本節(jié)點: parent', [
e('div', {'class': 'child'}, [
e('p', '這里是文本節(jié)點: child x child')
])
]))
console.log($node.tagName) // DIV
// 其他可能用到的方式
function template_submit (data) {
var $event_node = SatoriDom.compile(e('input', {'class': 'i-have-event', 'type': 'submit'}))
$event_node.addEventListener('click', function (event) {
console.log('You click me:' + data.content)
})
return SatoriDom.compile(e('form', {'class': 'this-is-a-form'}, [
$event_node
]))
}
serval/script/harusame-template.js
存放了一些模板徙瓶,把與創(chuàng)建DOM 相關(guān)的代碼抽離出來毛雇,避免其在邏輯代碼中占用很多篇幅。serval/script/harusame-cursor.js
光標(biāo)會被單獨抽象成一個類侦镇。serval/script/harusame-serval.js
綁定整個編輯器的事件灵疮,以及邏輯的處理,最重要的部分壳繁。serval/style/normalize.scss
見 normalize.scss 官網(wǎng)震捣,這里改了后綴只是方便后綴格式相同....serval/style/harusame-code.scss
是目錄serval/style/code/
下所有需要高亮的語言的樣式的 入口,這樣便于以后做其他語言的樣式擴展闹炉。這里先有能力解決 js 的語法高亮再想著其他語言吧蒿赢。serval/style/harusame-serval.scss
描述了編輯器樣式。
先直接描繪出成型后的編輯器
對著已有的優(yōu)秀的編輯器觀察剩胁,把看到的東西抽象成幾部分诉植,再根據(jù)這些編寫成 DOMs 祥国。
這里對著 Sublime Text 3 截了一張圖 1-2:
挺小也挺簡單的一張圖昵观,但是包含了想做的編輯器的大部分所需 DOMs,或者稱他為組件舌稀,這里說下明確當(dāng)前要做的部分:
- 行號 ( 6 ~ 15)
- 光標(biāo) ( 第七行最后的白色豎線)啊犬,與多重光標(biāo)(7, 9, 15)
- 選中行提示(比如第七行)
- 選中內(nèi)容提示(比如第九行,第 13 ~ 15 行)
這里插入一點壁查,以下內(nèi)容會是顯而易見的觉至,但是也是編輯器組成中最最重要的,盡量把這些理所當(dāng)然的也把它梳理出來睡腿,畢竟他們也需要 DOM 來顯示
- 行的內(nèi)容(比如第七行的
state = {
) - 編輯器樣式(比如這里深灰色语御,行號的灰色,非關(guān)鍵詞的白色)
以下是一些肉眼看不到的
- 行能夠接受鍵盤輸入
- 點擊一個位置席怪,光標(biāo)會自動偏移到該行中应闯,離點擊字符最近的位置。
- 行號無法選中
至于一些其他的細(xì)節(jié):代碼高亮挂捻,成對的括號提示等碉纺,這里會放在以后再做/說。
于是根據(jù)這些梳理好的內(nèi)容,轉(zhuǎn)換成 DOMs:
-
編輯器容器
設(shè)置編輯器的背景顏色等骨田,也是存放 其他容器的容器(父節(jié)點) -
光標(biāo)容器
存放 所有光標(biāo) 的元素節(jié)點 -
行容器
存放 一行 的元素節(jié)點耿导,包括它的行號,以及行的內(nèi)容 -
選擇容器
存放比如 當(dāng)前選擇行态贤,選擇內(nèi)容 的背景高亮的元素節(jié)點 -
鍵盤輸入事件接收器
正如之前所說(好像說了)舱呻,一個div
本身是不能接受鍵盤事件的,需要一個接受鍵盤事件的容器悠汽,再將事件的處理邏輯與相應(yīng)的元素節(jié)點綁定狮荔。
以上就是需要的 DOMs 了,包含關(guān)系也基本沒啥問題了:
文件路徑 serval/index
<div id="input-container" class="input-container">
<!-- 創(chuàng)建一個 serval 專屬的區(qū)域介粘,且總是鋪滿 編輯器的容器 -->
<div class="serval theme-harusame">
<!-- 包裹區(qū)域殖氏,總是由 行 的高度所撐開 -->
<div class="serval-container">
<!-- 所有 行 的 容器 -->
<div class="line-container">
<div class="line">
<div class="line-number-wrap">
<span class="line-number">1</span>
</div>
<!-- 在pre中,添加使用等寬字體的 buff姻采,使用等寬字體是為了之后雅采,便于文字寬度的計算 -->
<pre class="code-wrap">
<!-- 因為一般放的是代碼,codeMirror 是用的 span慨亲,試試 code -->
<code class="code-content">const PI = 3.1415</code>
</pre>
</div>
<div class="line">
<div class="line-number-wrap">
<span class="line-number">2</span>
</div>
<pre class="code-wrap">
<code class="code-content">console.info('PI', PI)</code>
</pre>
</div>
<div class="line">
<div class="line-number-wrap">
<span class="line-number">3</span>
</div>
<pre class="code-wrap">
<code class="code-content"></code>
</pre>
</div>
</div>
<!-- 接受鍵盤事件 的容器 的容器 -->
<div class="inputer-container">
<textarea class="inputer">
<!-- 這里使用一個 看不見的(并不是隱藏哦) textarea 來接受鍵盤事件 -->
</div>
<!-- 所有 光標(biāo) 的容器 -->
<div class="cursor-container">
<i class="fake-cursor blink"></i>
</div>
<!-- 選擇行 提示 -->
<div class="selected-container">
<div class="selected-line"></div>
</div>
</div>
</div>
</div>
文件路徑 serval/style/harusame-serval.css
這里就直接給了編譯后的
還是挺暴力的婚瓜,所有都直接嵌套,防止命名沖突
.serval {
position: relative;
height: 100%;
}
.serval .serval-container {
height: 100%;
}
.serval .serval-container .line-container {
margin-left: 50px;
font-size: 0;
}
.serval .serval-container .line-container .line .line-number-wrap {
position: absolute;
left: 0;
text-align: right;
}
.serval .serval-container .line-container .line .line-number-wrap .line-number {
display: inline-block;
padding-right: 15px;
box-sizing: border-box;
width: 50px;
height: 20px;
line-height: 20px;
font-size: 12px;
}
.serval .serval-container .line-container .line .code-wrap .code-content {
display: block;
height: 20px;
line-height: 20px;
font-size: 12px;
}
.serval .serval-container .cursor-container {
position: absolute;
top: 0;
margin-left: 50px;
}
.serval .serval-container .cursor-container .fake-cursor {
display: block;
position: absolute;
left: 0;
top: 0;
width: 0;
height: 18px;
margin-top: 1px;
}
.serval .serval-container .selected-container {
position: absolute;
top: 0;
width: 100%;
}
.serval .serval-container .selected-container .selected-line {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 20px;
}
.serval .serval-container .inputer-container {
position: absolute;
top: 0;
margin-left: 50px;
}
.serval .serval-container .inputer-container .inputer {
position: absolute;
left: 0;
top: 0;
border: 0;
resize: none;
width: 0;
height: 20px;
line-height: 20px;
opacity: 0;
}
.theme-harusame {
background-color: rgba(0, 0, 0, 0.8);
}
.theme-harusame .serval-container .line-container {
font-family: Consolas;
}
.theme-harusame .serval-container .line-container .line .line-number-wrap .line-number {
color: white;
cursor: default;
}
.theme-harusame .serval-container .line-container .line .code-wrap {
cursor: text;
}
.theme-harusame .serval-container .line-container .line .code-wrap .code-content {
font-family: Consolas;
color: white;
}
.theme-harusame .serval-container .cursor-container .fake-cursor {
border-right: 1px solid rgba(255, 255, 255, 0.9);
}
.theme-harusame .serval-container .selected-container .selected-line {
background-color: rgba(255, 255, 255, 0.15);
}
效果圖刑棵,見圖1-3 巴刻,嗯嗯,不錯的感覺蛉签。
HTML 方面應(yīng)該沒有什么問題胡陪,就把它們轉(zhuǎn)換成為模板,寫入 template.js中碍舍,并且刪掉 index.html 中剛才寫的 HTML標(biāo)簽柠座。
文件路徑 serval/script/harusame-template.js
;
(function () {
Template = {
/**
* 編輯器
*/
editor: function () {
var $line_container = SatoriDom.compile(e('div', {'class': 'line-container'}))
var $inputer_container = SatoriDom.compile(e('div', {'class': 'inputer-container'}))
var $cursor_container = SatoriDom.compile(e('div', {'class': 'cursor-container'}))
var $selected_container = SatoriDom.compile(e('div', {'class': 'selected-container'}))
var $serval_container = SatoriDom.compile(e('div', {'class': 'serval-container'}, [
$inputer_container,
$selected_container,
$cursor_container,
$line_container
]))
var $fragment = SatoriDom.compile(
e('div', {'class': 'serval theme-harusame'}, [
$serval_container
])
)
return {
$editor: $fragment,
nodes: {
$serval_container: $serval_container,
$line_container: $line_container,
$inputer_container: $inputer_container,
$cursor_container: $cursor_container,
$selected_container: $selected_container
}
}
},
/**
* 行
* @param line_number {string} 行號
* @param initial_content {string} 該行初始內(nèi)容
*/
line: function (params) {
console.info('params', params)
return SatoriDom.compile(
e('div', {'class': 'line'}, [
e('div', {'class': 'line-number-wrap'}, [
e('span', {'class': 'line-number'}, params.line_number)
]),
e('div', {'class': 'code-wrap'}, [
e('code', {'class': 'code-content'}, params.initial_content || '')
])
])
)
},
/**
* 當(dāng)前選擇行
*/
selectedLine: function () {
return SatoriDom.compile(
e('div', {'class': 'selected-line'})
)
},
/**
* 光標(biāo)
*/
cursor: function () {
return SatoriDom.compile(
e('i', {'class': 'fake-cursor blink'})
)
},
/**
* 鍵盤事件接收器
*/
inputer: function () {
return SatoriDom.compile(
e('textarea', {'class': 'inputer'})
)
}
}
window.Template = Template
})()
文件路徑 serval/script/cursor.js
;
(function () {
var Cursor = function () {
this.$ref = null
this._generateCursor()
}
Cursor.prototype = {
constructor: Cursor,
/**
* 創(chuàng)建一個游標(biāo)對象
*/
_generateCursor: function () {
this.$ref = SatoriDom.compile(
e('i', {'class': 'fake-cursor'})
)
}
}
window.Cursor = Cursor
})()
文件路徑 serval/script/serval.js
;
(function () {
/**
* 1. 存放所有的存在游標(biāo)
*/
var Serval = function (config) {
/* 1 */
this.cursor_list = []
this._generateEditor(config)
this._generateInputer()
this._generateCursor()
this._generateSelectedLine()
this._generateLine() // 測試用!F稹妈经!
}
Serval.prototype = {
constructor: Serval,
/**
* 生成編輯器的主要 DOM結(jié)構(gòu)
*/
_generateEditor: function (config) {
var temp = Template.editor()
var nodes = temp.nodes
this.$serval_container = nodes.$serval_container
this.$line_container = nodes.$line_container
this.$inputer_container = nodes.$inputer_container
this.$cursor_container = nodes.$cursor_container
this.$selected_container = nodes.$selected_container
config['editor-container'].appendChild(temp.$editor)
},
/**
* 生成鍵盤事件接收器,并渲染
*/
_generateInputer: function () {
var $inputer = Template.inputer()
this.$inputer = $inputer
this.$inputer_container.appendChild($inputer)
},
/**
* 生成一個光標(biāo)捧书,并渲染
* 1. 創(chuàng)建 cursor 實例
* 2. 持久化該 cursor 實例
* 3. 得到該 cursor 實例的元素節(jié)點吹泡,渲染到 光標(biāo)容器 中
*/
_generateCursor: function () {
var cursor = new Cursor() /* 1 */
this.cursor_list.push(cursor) /* 1 */
this.$cursor_container.appendChild(cursor.$ref) /* 3 */
},
/**
* 生成一行,并渲染
*/
_generateLine: function () {
var $line = Template.line({line_number: '1', initial_content: '初始化內(nèi)容'})
this.$line_container.appendChild($line)
},
/**
* 生成當(dāng)前選擇行的背景顏色提示经瓷,并渲染
*/
_generateSelectedLine: function () {
var $selected_line = Template.selectedLine()
this.$selected_line = $selected_line
this.$selected_container.appendChild($selected_line)
}
}
window.Serval = Serval
})()
效果圖爆哑,見圖1-4
嗯嗯,好像沒什么毛病...
隨手框選了一下了嚎,發(fā)現(xiàn)泪漂,這幾個字沒法選中廊营,有點不符合預(yù)期,在之后做選取內(nèi)容時萝勤,肯定會坑露筒,所以立馬填了。
額...因為在當(dāng)前層疊上下文中敌卓,選擇行<.selected_container>
的層疊樣式最大(他在最下面) 并且 他有position: absolute
的屬性慎式,導(dǎo)致覆蓋了<.line_container>
,所以
- 去
template.js
中調(diào)整一下位置趟径,令他們的覆蓋情況更合邏輯瘪吏。 - 為
<.line_container>
添加一個position: relative
屬性 - 另外
2
的副作用就是,由于<.line_container>
具備了定位屬性蜗巧,行的left: 0
掌眠,得手工設(shè)置為left: -50px
了,不然布局會亂哦
做這些理論上就可以了幕屹,當(dāng)然如果懶得改的話蓝丙,設(shè)置z-index
,pointer-events
之類的也可以望拖,這里就不記了:
文件路徑 serval/script/harusame-template.js
改成這樣 ↓
var $fragment = SatoriDom.compile(
e('div', {'class': 'serval theme-harusame'}, [
e('div', {'class': 'serval-container'}, [
$inputer_container,
$selected_container,
$cursor_container,
$line_container
])
])
)
文件路徑 serval/style/harusame-serval.scss
.line-container {
// 添加一條
position: relative;
}
.line-number-wrap {
// 添加一條
left: -50px;
// 這里的 50px 取決于 .line-number 的 width 屬性
}
光標(biāo)
先說說光標(biāo)的邏輯渺尘。
可以在 #0 節(jié)的例子中了解到
// ...
... substring(0, logicalX) ...
// ...
編輯器實際上通過 字符的位置 來添加,刪除文本/代碼说敏,這里所說的字符的位置鸥跟,我自作主張把它稱為 邏輯位置,對應(yīng)到 Cursor實例
中盔沫,給他命名為 logical
医咨,相應(yīng)地,也有 物理位置(視圖位置)迅诬,對應(yīng)到 Cursor實例 的DOM
中腋逆,給他命名為 psysical
。
logicalY
決定了光標(biāo)在編輯器中的 行號
psysicalY
決定了光標(biāo) DOM 的 top
值
logicalX
決定了光標(biāo)在編輯器中的 列號侈贷,或者說 第 logicalX 個字符
psysicalX
決定了光標(biāo) DOM 的 left
值
除了這些,光標(biāo)還需要一個保存選區(qū)的屬性等脂,這里命名為selection
俏蛮,
selection
是一個對象,它包含一個起始點坐標(biāo)start
上遥,與一個終點坐標(biāo)end
舉個栗子搏屑,對于圖 1-5:
為了計數(shù)方便,刪除了部分縮進(jìn)粉楚,以及這里的縮進(jìn)都是 4個空格辣恋。
有以下光標(biāo):
- 第三行的光標(biāo)
cursor_1
- 第四行的光標(biāo)
cursor_2
- 第五行的光標(biāo)
cursor_3
- 第六行的光標(biāo)
cursor_4
它們分別有以下屬性:
(如果看懂了文字說明亮垫,以下代碼就隨便看看)
cursor_1 = {
logicalY: 2, /* 從零開始計算 */
psysicalY: 40, /* 約定一行的高度為 20px */
logicalX: 2, /* 從零開始計算 */
psysicalX: 14,
/* 字符'l',字符'e'的寬度伟骨,通過標(biāo)尺類工具可以得到一個字符(英文)的寬度約等于 7px
* 手工寫死的字符寬度面對不同的情況或者說樣式上變化饮潦,會比較難維護(hù)
* 接下來馬上會說怎么使用瀏覽器的計算,雖然也不怎么優(yōu)雅......_(:3」∠)...
* /
selection: null
}
cursor_2 = {
logicalY: 3,
psysicalY: 60,
logicalX: 0,
psysicalX: 0,
selection: null
}
cursor_3 = {
logicalY: 4,
psysicalY: 80,
logicalX: 5,
psysicalX: 35, // 5 * 7
selection = {
start: {
logicalY: 4,
psysicalY: 80,
logicalX: 0,
psysicalX: 0
},
/* end: {}
* 寫到這里的時候發(fā)現(xiàn)携狭,end 的位置總是與當(dāng)前位置相同继蜡,于是決定不要這個冗余的部分,
* 雖然與 start 相對應(yīng)的 end 缺失了逛腿,可能會 '引起不適'稀并,
* 但是暫時覺得沒必要多出一份冗余數(shù)據(jù)
* 所以這里改變以下 selection的寫法 ↓,把 selection 刪了单默,只留下 selection_start
*/
}
selection_start = {
logicalY: 4,
psysicalY: 80,
logicalX: 0,
psysicalX: 0
}
}
cursor_4 = {
logicalY: 5,
psysicalY: 100,
logicalX: 4,
/*
* 剛好四個空格碘举,觀察仔細(xì)會發(fā)現(xiàn),圖1-5 的距離不會是 4格搁廓,
* 這里因為偷懶用了 tab進(jìn)行縮進(jìn)殴俱,導(dǎo)致如此。
* 不要在意細(xì)節(jié)..._(:3」∠)...啊枚抵,不對线欲,編程還是得很注意細(xì)節(jié)的
*/
psysicalX: 28, /* 空格也約為 7px */
selection_start = {
logicalY: 5,
psysicalY: 100,
logicalX: 4,
psysicalX: 28
}
}
如果看完了甚至沒看上面的代碼,都可以 顯然 得到:
psysicalY = line_height * logicalY
logicalY = psysicalY / line_height
$current_line = document.querySelector(SIGN_LINE + logicalY)
psysicalX = single_byte_length * 7 + double_byte_length * 12
logicalX = single_byte_length + double_byte_length
這里的
line_height
汽摹,不是指css
中的line-height
哦李丰,是指 該行的高度-
這里的
SIGN_LINE
,是一個自己約定的名字逼泣,這里傾向于使用id
趴泌,而不是class
,個人覺得理由如下:1.每行都是唯一的
2.性能稍微高一點
3.可以利用錨點
方便地跳轉(zhuǎn)到該行位置
比如 github
中的代碼編輯器(也可以看 codeMirror
)
-
single_byte_length
這個變量名字譯為單字節(jié)字符的長度(數(shù)量)拉庶,同理double_byte_length
變量名字譯為雙字節(jié)字符的長度(數(shù)量)嗜憔。
(這個名字我不清楚對不對,之后再查閱下文獻(xiàn)氏仗。
為什么這樣分呢吉捶?
因為在計算光標(biāo)的psysicalX
時,需要計算字符的 寬度皆尔,所以根據(jù)寬度來分呐舔。
在 font-size: 12px
的前提下,
單字節(jié)字符的寬度慷蠕,比如數(shù)字珊拼,英文字符,半角標(biāo)點符號
a b c 1 2 3 . , /
等等 都約為 7px
雙字節(jié)字符的寬度流炕,比如漢字澎现,日語仅胞,全角標(biāo)點符號
你 好 啊 こ は お ア 。剑辫,干旧、
等等 都約為 12px
說明就到這里。
之所以把這些類似公式一樣的東西列出來揭斧,就是想說它們之間總是一一對應(yīng)莱革,改變其中一個讹开,另外一個也相應(yīng)地改變。這個情況闹击,是不是有點類似于雙向綁定
的概念呢。提到雙向綁定成艘,在如今赏半,前端大佬們有個比較好的處理方法就是使用Object.defineProperty
哦断箫。
接下來終于可以開始寫代碼。
計算光標(biāo)位置
首先 先去serval/script/harusame-serval.js
中提供事件
文件位置 serval/script/harusame-serval.js
為了能顯示更多的有效內(nèi)容秋冰,先把寫過的代碼在這里刪掉了仲义。
;
(function () {
var Serval = function (config) {
this._bindMouseEvent()
}
Serval.prototype = {
/**
* 綁定各種鼠標(biāo)事件
*/
_bindMouseEvent: function () {
var self = this
/**
* addEventListener 是指自己寫的方法剑勾,見最下面
* mousedown 時,就對光標(biāo)位置進(jìn)行計算
*/
addEventListener(self.$serval_container, 'mousedown', function (event) {
console.log(event) // 先看看 event 中有什么好用的屬性暂刘。
})
},
}
/**
* 可能會對 addEventListener 進(jìn)行一些兼容處理
* 實際上并沒有處理捂刺,不過也好,至少留一條后路...
*/
function addEventListener (v_el, v_type, v_callback) {
v_el.addEventListener(v_type, v_callback)
}
window.Serval = Serval
})()
先看看 event 中有什么好用的屬性芝发。
這里說一下由于一些事件在不同的瀏覽器行為是不同的苛谷,如果要兼容某個版本的瀏覽器腹殿,所以最好調(diào)試的時候看看預(yù)先各個瀏覽器的事件行為溜哮。這里在 Firefox 以及 Google 是沒什么差異的构挤,所以就不截圖了萝究。
這里可以看到有二個 layer
前綴的屬性婴栽,它總是能得到 相對于 event.target
元素的 點擊位置爱谁。這個特性就很好用访敌!
于是決定使用 event.layerY
和 event.layerX
來進(jìn)行光標(biāo)位置的計算衣盾!
這里要插一句_(:3」∠)...势决,在 demo 中 Y的位置 是使用 event.target.id.split[SIGN][1]
來獲得的阻塑,但是在以后的開發(fā)中,記得會出現(xiàn)一些麻煩的判斷陈莽,所以這里用 layerY
嘗試一下据悔。嘛,各有利弊朱盐,layerY
的話兵琳,也可能得考慮padding margin height line-height
等 css
造成的計算偏差骇径。
位置: serval/script/harusame-serval.js
為了突出主要說明部分,刪掉了其他代碼
;
(function () {
var Serval = function (config) {
this._bindMouseEvent()
}
Serval.prototype = {
constructor: Serval,
/**
* 綁定各種鼠標(biāo)事件
*/
_bindMouseEvent: function () {
var self = this
/**
* addEventListener 是指自己寫的方法清女,見最下面
* 當(dāng) mousedown 時嫡丙,就對光標(biāo)位置進(jìn)行計算
*/
addEventListener(self.$serval_container, 'mousedown', function (event) {
self.allocTask(function (v_cursor) {
v_cursor.psysicalY = event.layerY
v_cursor.psysicalX = event.layerX
})
})
},
/**
* 為所有光標(biāo)分配任務(wù)
* 對光標(biāo)操作時,統(tǒng)一通過這個接口
* 這里約定必須傳入一個回調(diào)函數(shù)拥刻,所以不使用 v_task && v_task() 進(jìn)行判斷
*/
allocTask: function (v_task) {
var self = this
for (var i = 0, len = self.cursor_list.length; i < len; i++) {
v_task(self.cursor_list[i])
}
},
}
})()
做好了入口般哼,接下來去 serval/script/harusame-cursor.js
寫邏輯哦惠窄。
先寫logicalY
與 psysicalY
部分
文件位置 serval/script/harusame-cursor.js
;
(function () {
/**
* 這里先約(寫)定(死) 行高
*/
var LINE_HEIGHT = 20
/**
* 1. 光標(biāo)本身的元素節(jié)點
* 2. 光標(biāo)所在行的元素節(jié)點
*/
var Cursor = function (config) {
this.$ref = null /* 1 */
this.$line = null /* 2 */
this._logicalY = 0
this._logicalX = 0
this._psysicalY = 0
this._psysicalX = 0
this.selection_start = null
this._generateCursor()
this._setObserver()
}
Cursor.prototype = {
constructor: Cursor,
/**
* 創(chuàng)建一個游標(biāo)對象
*/
_generateCursor: function () {
this.$ref = SatoriDom.compile(
e('i', {'class': 'fake-cursor'})
)
},
/**
* 綁定 邏輯位置 與 物理位置 之間的關(guān)系
*/
_setObserver: function () {
/**
* 這里的 self 由于也是 js關(guān)鍵字睬捶,所以會高亮
* self 原本指向 window,一般用不到
*/
var self = this
/**
* 1. 這里賦值的是 _logicalY 哦臀晃,下面也是
* 2. 更新 psysicalY 的值
* 3. 更新 DOM 位置
* 4. 寫到這里發(fā)現(xiàn)有點問題......
*/
Object.defineProperty(self, 'logicalY', {
set: function (v_logicalY) {
self._logicalY = v_logicalY /* 1 */
self._psysicalY = self.calcPsysicalY(v_logicalY) /* 2 */
self.setY(self._psysicalY) /* 3 */
self.$line = document.getElementById(LINE) /* 4 */
},
get: function () {
return self._logicalY
}
})
Object.defineProperty(self, 'psysicalY', {
set: function (v_psysicalY) {
self.logicalY = self.calcLogicalY(v_psysicalY)
},
get: function () {
return self._psysicalY
}
})
},
setY: function (v_psysicalY) {
this.$ref.style.top = v_psysicalY + 'px'
},
/**
* 計算 物理 Y
*/
calcPsysicalY: function (v_logicalY) {
return v_logicalY * LINE_HEIGHT
},
/**
* 計算 邏輯 Y
*/
calcLogicalY: function (v_psysicalY) {
return parseInt(v_psysicalY / LINE_HEIGHT)
}
}
window.Cursor = Cursor
})()
這里可以看看效果徽惋,截圖軟件截不到鼠標(biāo)_(:3」∠)...险绘。
但是也注意到了設(shè)計上的問題:在self.logicalY
中誉碴,需要用到 $line
黔帕,這個東西不應(yīng)該分在Cursor
中,畢竟他不屬于光標(biāo)呐芥,以及LINE_HEIGHT
屬性奋岁,也不屬于闻伶。甚至在接下來的 self.logicalX
中,也需要用到 $line
铡买,這個時候霎箍,不如把與 行 有關(guān)的,再單獨抽象成一個類會比較合適景埃。