為網(wǎng)頁實(shí)現(xiàn)mini-map效果

minimap效果的實(shí)現(xiàn)

在項(xiàng)目中墨技,需要制作出縮略圖的效果斜姥。效果如下:


minimap效果.gif

在firefox可以使用 element屬性實(shí)現(xiàn)該效果蜈亩。(其它瀏覽器暫不支持)蒸眠。這里使用iframe來達(dá)到更好的兼容性。

效果分析

minimap主要是對主體內(nèi)容進(jìn)行映射询件。主體html如下:

<body>
    <div class="content">
        <p>
            文章內(nèi)容
        </p>
        <img src="..">
    </div>
</body>

我們需要將需要的html內(nèi)容燃乍,嵌入到iframe中。

# 這里獲取整個document下的html
let html = document.documentElement.outerHTML
# 將html內(nèi)容寫入到目標(biāo)iframe中
targetIframe.wirte(html)

整體邏輯是這樣宛琅。接著實(shí)現(xiàn)點(diǎn)擊橘沥、滾動、拖拽等事件夯秃。如果有大量交互行為的頁面座咆,需要對minimap做更多的優(yōu)化。

顯示效果的實(shí)現(xiàn)

codepen地址

考慮頁面加載的延遲效果仓洼,我們將minimap的js文件放在body的最后進(jìn)行引用(當(dāng)然介陶,建議動態(tài)頁面考慮其他的方式進(jìn)行實(shí)現(xiàn))。

minimap所處的div結(jié)構(gòu)應(yīng)該如下:

<div class="slider">
    <iframe class="slider__content">
    </div>
    <div class="slider__size">
    </div>
    <div class="slider__controller">
    </div>
</div>

iframe的作用就是映射主體的內(nèi)容的html色建。后面的 slider__size slider__controller有什么用哺呜?

我們對比一下有無后兩個div的差別:

效果對比示意

前一個效果沒有選擇框,無法進(jìn)行滾動和拖拽箕戳。

實(shí)際上某残,slider__size控制顯示區(qū)域的大小,slider__controller顯示目前顯示的區(qū)域范圍陵吸。

計算顯示區(qū)域

minimap的顯示區(qū)域應(yīng)該達(dá)到以下效果

  • iframe中內(nèi)容的縱橫比應(yīng)該和目標(biāo)區(qū)域的縱橫比相同
  • iframe中內(nèi)容應(yīng)該進(jìn)行等比例縮放

那么玻墅, 我們假設(shè)目標(biāo)內(nèi)容的寬為Xpx,高為Ypx壮虫,iframe的高為 x px澳厢, 高度為 y px环础。縮放比例為 scale 那么就有:

x / y = X / Y

scale = x / X

js的實(shí)現(xiàn)如下

let bodyWidth = body.clientWidth,
    # 整體內(nèi)容的長寬比
    bodyRatio = docEl.clientHeight / docEl.clientWidth,
    # window的長寬比
    winRatio = win.innerHeight / win.innerWidth;

# 設(shè)置整個slider區(qū)域占父級區(qū)域的寬度
slider.style.width = '10%';
# 獲取scale的值
realScale = slider.clientWidth / bodyWidth;
# 進(jìn)行等比例縮放
sliderContent.style.transform = `scale(${realScale})`; 
# 為content設(shè)置一個寬高剩拢。
sliderContent.style.width = (100 / realScale) + '%';
sliderContent.style.height = (100 / realScale) + '%';

這里有一個問題:

  • 為什么 sliderContent的值這樣設(shè)置线得?

slider__size是如何控制顯示區(qū)域的大小

首先,我們知道padding區(qū)域是一個透明的區(qū)域徐伐。我們可以通過控制padding區(qū)域的大小進(jìn)而就能控制需要顯示的區(qū)域贯钩。

  • slider__size疊加到 slider__content上。

    這里只需要將 slider__content定位屬性設(shè)置為absolute

  • 設(shè)置 slider__sizepadding的寬高

sliderSize.style.paddingTop = (bodyRatio * 100)  + '%';

slider__controller做一個定位框

這里應(yīng)用 padding屬性和 boder屬性办素,以及 translate屬性角雷。

  • padding屬性保證定位框透明
  • boder屬性設(shè)置邊框
  • translate屬性控制定位框的移動

定位框的大小比例需要和window的長寬比例保持一致:

controller.style.paddingTop = (winRatio * 100) + '%';

當(dāng)頁面移動時,我們通過設(shè)置translate屬性值來定位到minimap上的位置摸屠。

 controller.style.transform = `translate(${win.pageXOffset * realScale}px, ${win.pageYOffset * realScale}px)`;

為minimap增加事件效果

我們需要為minimap增加以下效果:

  • 點(diǎn)擊minimap谓罗,主體內(nèi)容自動移動到指定位置
  • 頁面滾動粱哼,minimap也隨之滾動
  • 拖拽定位框季二,頁面也隨之進(jìn)行移動

一般的js程序員,基本會采用以下事件組合

  • 鼠標(biāo)按下(mousedown)觸發(fā)移動(scrollTo)
  • 鼠標(biāo)移動(mouseMove)觸發(fā)滾動(scrollBy)
  • 鼠標(biāo)松開(mouseup)停止觸發(fā)滾動(scrollBy)
  • 鼠標(biāo)不在minimap區(qū)域揭措,停止觸發(fā)移動(scrollTo)
let mouseX = 0,
    mouseY = 0,
    mouseDown = false;

function pointerDown(e) {
    e.preventDefault();
    mouseDown = true;
    mouseX = e.clientX;
    mouseY = e.clientY;
    let offsetX = ((mouseX - slider.offsetLeft) - (controller.clientWidth/2)) / realScale;
    let offsetY = ((mouseY - slider.offsetTop) - (controller.clientHeight / 2)) / realScale;
    win.scrollTo(offsetX, offsetY);
}

slider.addEventListener('mousedown', pointerDown);

function pointerMove(e) {
    if (mouseDown) {
        x = e.clientX;
        y = e.clientY;
        win.scrollBy((x - mouseX) / realScale, ((y - mouseY) / realScale));
        mouseX = x;
        mouseY = y;
    }
}

win.addEventListener('mousemove', pointerMove);

function pointerReset(e) {
    mouseDown = false;
}
win.addEventListener('mouseup', pointerReset);

function pointerLeave(e) {
    if ( e.target === body) {
        mouseDown = false;
    }
}

當(dāng)然胯舷,制作復(fù)雜頁面的minimap的時候,會涉及到更加復(fù)雜的邏輯交互绊含∩K唬可以采用reactJS來進(jìn)行事件的管理,這里不贅述躬充。有需要的同學(xué)可以留言逃顶,后期會考慮出一系列reactJS實(shí)際應(yīng)用的例子。

擴(kuò)展閱讀

使用element實(shí)現(xiàn)minimap效果

mouse事件

mini-map實(shí)現(xiàn)源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末充甚,一起剝皮案震驚了整個濱河市以政,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伴找,老刑警劉巖盈蛮,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異技矮,居然都是意外死亡抖誉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門衰倦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來袒炉,“玉大人,你說我怎么就攤上這事樊零∈嵝樱” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長十性。 經(jīng)常有香客問我叛溢,道長,這世上最難降的妖魔是什么劲适? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任楷掉,我火速辦了婚禮,結(jié)果婚禮上霞势,老公的妹妹穿的比我還像新娘烹植。我一直安慰自己,他們只是感情好愕贡,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布草雕。 她就那樣靜靜地躺著,像睡著了一般固以。 火紅的嫁衣襯著肌膚如雪墩虹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天憨琳,我揣著相機(jī)與錄音诫钓,去河邊找鬼。 笑死篙螟,一個胖子當(dāng)著我的面吹牛菌湃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播遍略,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼惧所,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了绪杏?” 一聲冷哼從身側(cè)響起下愈,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寞忿,沒想到半個月后驰唬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腔彰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年叫编,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霹抛。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡搓逾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出杯拐,到底是詐尸還是另有隱情霞篡,我是刑警寧澤世蔗,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站朗兵,受9級特大地震影響污淋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜余掖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一寸爆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盐欺,春花似錦赁豆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至粉洼,卻和暖如春节预,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背漆改。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工心铃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留准谚,地道東北人挫剑。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像柱衔,于是被迫代替她去往敵國和親樊破。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359