從移動(dòng)端click到搖一搖

以前聽到前輩們說移動(dòng)端盡量不要使用click阁谆,click會(huì)比較遲鈍,能用touchstart還是用touchstart记某。但是用touchstart會(huì)有一個(gè)問題痊夭,用戶在滑動(dòng)頁面的時(shí)候要是不小心碰到了相關(guān)元素也會(huì)觸發(fā)touchstart舞丛,所以兩者都有缺點(diǎn)耘子。那怎么辦呢?
首先為什么移動(dòng)端的click會(huì)遲鈍呢瓷马?從谷歌的開發(fā)者文檔《300ms tap delay, gone away》可以找到答案:

For many years, mobile browsers applied a 300-350ms delay between touch end and click while they waited to see if this was going to be a double-tap or not, since double-tap was a gesture to zoom into text.

大意是說因?yàn)橐苿?dòng)端要判斷是否是雙擊拴还,所以單擊之后不能夠立刻觸發(fā)click,要等300ms欧聘,直到確認(rèn)不是雙擊了才觸發(fā)click。所以就導(dǎo)致了click有延遲端盆。

更為重要的是怀骤,文檔里面還提到在2014年的Chrome 32版本已經(jīng)把這個(gè)延遲去掉了费封,如果有一個(gè)meta標(biāo)簽:

<meta name="viewport" content="width=device-width">

即把viewport設(shè)置成設(shè)備的實(shí)際像素,那么就不會(huì)有這300ms的延遲蒋伦,并且這個(gè)舉動(dòng)受到了IE/Firefox/Safari(IOS 9.3)的支持弓摘,也就是說現(xiàn)在的移動(dòng)端開發(fā)可以不用顧慮click會(huì)比較遲鈍的問題。

如果設(shè)置initial-scale=1.0痕届,在chrome上是可以生效韧献,但是Safari不會(huì):

<meta name="viewport" content="initial-scale=1.0">

還有第三種辦法就是設(shè)置CSS:

html{
    touch-action: manipulation;
}

這樣也可以取消掉300ms的延遲,Chrome和Safari都可以生效研叫。
click是在什么時(shí)候觸發(fā)的呢锤窑?來研究一下click/touch事件的觸發(fā)先后順序。

1. click/touch觸發(fā)順序

用以下的html代碼來實(shí)驗(yàn):

<!DOCType html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1.0">
</head>
<body>
    <div id="target" style="margin-top:20px;width:200px;height:200px;background-color:#ccc">hello, world</div>
    <script>
!function(){
    var target = document.getElementById("target");
    var body = document.querySelector("body");
    var touchstartBeginTime = 0;
    function log(event){
        if(event.type === "touchstart") touchstartBeginTime = Date.now();
        console.log(event.type, Date.now() - touchstartBeginTime);
    }
    target.onclick = log;
    target.ontouchstart = log;
    target.ontouchend = log;
    target.ontouchmove = log;
    target.onmouseover = log;
    target.onmousedown = log;
    target.onmouseup = log;
}();
    </script>
</body>
</html>

用一臺(tái)iPhone6 (IOS 10)的手機(jī)連接電腦的Safari做實(shí)驗(yàn)嚷炉,如下圖所示:

然后點(diǎn)擊灰色的target區(qū)域渊啰,用電腦的Safari進(jìn)行檢查,可以看到輸出結(jié)果:

可以看到click事件是在最后觸發(fā)的申屹,并且還看到300ms的延遲绘证,實(shí)際的執(zhí)行延遲要比這個(gè)大,因?yàn)闉g覽器的內(nèi)核運(yùn)行也需要消耗時(shí)間』┘ィ現(xiàn)在加上viewport的meta標(biāo)簽嚷那,再觀察結(jié)果,如下圖所示:

可以看到杆煞,300ms的延遲沒有了魏宽。
知道了click是在touchend之后觸發(fā)的,現(xiàn)在我們來嘗試一下實(shí)現(xiàn)一個(gè)tap事件索绪。

2. tap事件的實(shí)現(xiàn)

雖然已經(jīng)沒有太大的必要自行實(shí)現(xiàn)一個(gè)tap事件湖员,但是我們還是很好奇可以怎么實(shí)現(xiàn)一個(gè)能夠快速觸發(fā)的tap的事件?有兩個(gè)庫瑞驱,一個(gè)是zepto娘摔,另一個(gè)是fastclick,它們都可以解決點(diǎn)擊延遲的問題唤反。其中凳寺,zepto有一個(gè)自定義事件tap,它是一個(gè)沒有延遲的click事件彤侍。而fastclick是在touchend之后生成一個(gè)click事件肠缨,并立即觸發(fā)這個(gè)click,再取消原本的click事件盏阶。這兩者的原理都是一樣的晒奕,都是在touchend之后觸發(fā),一個(gè)是觸發(fā)它自己定義的tap事件,一個(gè)是觸發(fā)原生click脑慧。
這里有一個(gè)關(guān)鍵的問題魄眉,就是touchend之后不能夠每次都觸發(fā)tap,因?yàn)橛锌赡苡脩羰窃谏舷禄⒉皇窃邳c(diǎn)擊闷袒,不然的話直接監(jiān)聽touchstart就好了坑律。所以怎么判定用戶是點(diǎn)擊還是在上下滑呢?Zepto是用的位移偏差囊骤,即記錄下touchstart的時(shí)候的初始位移晃择,然后用touchend的時(shí)候的位移減掉初始位移的偏差,如果這個(gè)差值在30以內(nèi)也物,則認(rèn)為用戶是點(diǎn)擊宫屠,大于30則認(rèn)為是滑動(dòng)。而fastclick是用的時(shí)間偏差焦除,分別記錄touchstart和touchend的時(shí)間戳激况,如果它們的時(shí)間差大于700毫秒,則認(rèn)為是滑動(dòng)操作膘魄,否則是點(diǎn)擊操作乌逐。
Chrome又是怎么判斷用戶是點(diǎn)擊還是滑動(dòng)呢,筆者沒有去看安卓或者IOS Chrome的源碼创葡,找了下Chromium的源碼浙踢,它里面有一個(gè)resources的目錄,是Chrome自己頁面的代碼灿渴,如chrome://setting頁洛波,它是用html寫的。在這個(gè)里面有一個(gè)touch_handler.js骚露,它里面封裝了一些移動(dòng)端的手勢實(shí)現(xiàn)如tap蹬挤,tap是根據(jù)時(shí)間位移判斷是否要觸發(fā)tap,如下所示:

/**
   * The time, in milliseconds, that a touch must be held to be considered
   * 'long'.
   * @type {number}
   * @private
   */
  TouchHandler.TIME_FOR_LONG_PRESS_ = 500;

定義的時(shí)間為長時(shí)間按壓long press的時(shí)間閾值為500ms棘幸,在touchstart里面啟動(dòng)一個(gè)計(jì)時(shí)器:

this.longPressTimeout_ = window.setTimeout(
      this.onLongPress_.bind(this), TouchHandler.TIME_FOR_LONG_PRESS_);

onLongPress_: function() {
  this.disableTap_ = true;
}

如果超過了閾值500ms焰扳,就把一個(gè)標(biāo)志位disableTap_設(shè)置為true,然后在touchend里面误续,這個(gè)flag為true就不會(huì)觸發(fā)tap:

if (!this.disableTap_)
    this.dispatchEvent_(TouchHandler.EventType.TAP, touch);

相對(duì)于fastclick用兩個(gè)時(shí)間戳的方式吨悍,我感覺源碼的實(shí)現(xiàn)更為復(fù)雜,因?yàn)橐獑?dòng)一個(gè)計(jì)時(shí)器蹋嵌。

現(xiàn)在我們來實(shí)現(xiàn)一個(gè)按位移偏差判斷的tap育瓜。

要實(shí)現(xiàn)一個(gè)自定義事件,有兩種方式栽烂,第一種是像jQuery/Zepto一樣躏仇,自己封裝一個(gè)事件機(jī)制恋脚,第二種是調(diào)用原生的document.createEvent,然后再執(zhí)行div.dispatchEvent(event)钙态,這里我們使用第一種慧起。

為此先寫一個(gè)選擇器菇晃。如下代碼所示:

var $ = function(selector){
    var dom = null;
    if(typeof selector === "string"){
        dom = document.querySelectorAll(selector);
    } else if(selector instanceof HTMLElement){
        dom = selector;
    }   
    return new $Element(dom);
}
window.$ = $;

選擇器的名稱用$册倒,它是一個(gè)函數(shù),傳進(jìn)來的參數(shù)為選擇器或者dom元素磺送,如果是字符串的選擇器驻子,則調(diào)用querySelectorAll去獲取dom元素,如果它已經(jīng)是一個(gè)dom則不用處理估灿,最后返回一個(gè)$Element的封裝的實(shí)例崇呵,類似于jQuery對(duì)象。

現(xiàn)在來實(shí)現(xiàn)這個(gè)$Element的類馅袁,如下代碼所示:

class $Element{
    constructor(_doms){
        var doms = _doms.constructor === Array || _doms.constructor === NodeList ?
                   _doms : [_doms];
        this.doms = doms;
        this.init();
        for(var i = 0; i < doms.length; i++){
            this[i] = doms[i];
            if(!doms[i].listeners){
                doms[i].listeners = {}; 
            }   
        }   
    } 
}

$Element的構(gòu)造函數(shù)里面域慷,先判斷參數(shù)的類型,如果它不是一個(gè)數(shù)組或者是用querySelectorAll返回的NodeList類型汗销,則構(gòu)造一個(gè)dom數(shù)組犹褒。然后給這些dom對(duì)象添加一個(gè)listeners的屬性,用來存放事件的回調(diào)函數(shù)弛针。注意這里不是一個(gè)好的實(shí)踐叠骑,因?yàn)橐话悴煌扑]給原生對(duì)象添加?xùn)|西。但是從簡單考慮削茁,這里先用這樣的方法宙枷。

第8行代碼比較有趣,把this當(dāng)作一個(gè)數(shù)組茧跋,dom元素當(dāng)作這個(gè)數(shù)組的元素慰丛。這樣就可以通過索引獲取dom元素:

var value = $("input")[0].value;

但是它又不是一個(gè)數(shù)組,它沒有數(shù)組的sort/indexOf等函數(shù)瘾杭,它是一個(gè)$Element實(shí)例诅病,另一方面它又有l(wèi)ength,可以通過index獲取元素富寿,所以它是一個(gè)偽數(shù)組睬隶,這樣你就知道了arguments實(shí)例、jQuery對(duì)象這種偽數(shù)組是怎么來的页徐。

上面代碼還調(diào)了一個(gè)init苏潜,這個(gè)init函數(shù)用來添加tap事件:

init(){
    for(var i = 0; i < this.doms.length; i++){
        if(!this.doms[i].listeners){
            this.initTapEvent(this.doms[i]);
        }       
    }
}

在說tap事件之前,需要提供事件綁定和觸發(fā)的api变勇,如下所示:

on(eventType, callback){
    for(var i = 0; i < this.doms.length; i++){
        var dom = this.doms[i];
        if(!dom.listeners[eventType]){
            dom.listeners[eventType] = [];
        }       
        dom.listeners[eventType].push(callback);
    }
}

上面的on函數(shù)會(huì)給dom的listeners屬性添加相應(yīng)事件的回調(diào)恤左,每種事件類型都用一個(gè)數(shù)組存儲(chǔ)贴唇。而觸發(fā)的代碼如下所示:

trigger(eventType, event){ 
    for(var i = 0; i < this.doms.length; i++){
        $Element.dispatchEvent(this.doms[i], eventType, event); 
    }
}
static dispatchEvent(dom, eventType, event){ 
    var listeners = dom.listeners[eventType];
    if(listeners){
        for(var i = 0; i < listeners.length; i++){
            listeners[i].call(dom, event); 
        }       
    }
}

這段代碼也好理解,根據(jù)不同的事件類型去取回調(diào)函數(shù)的數(shù)組飞袋,依次執(zhí)行戳气。
現(xiàn)在重點(diǎn)來說一下怎么添加一個(gè)tap事件,即上面的initTapEvent函數(shù)巧鸭,如下代碼所示:

initTapEvent(dom){ 
    var x1 = 0, x2 = 0, y1 = 0, y2 = 0;
    dom.addEventListener("touchstart", function(event){

    });
    dom.addEventListener("touchmove", function(event){

    });
    dom.addEventListener("touchend", function(event){

    });
}

思路是這樣的瓶您,在touchstart的時(shí)候記錄x1和y1的位置:

dom.addEventListener("touchstart", function(event){
    var touch = event.touches[0];
    x1 = x2 = touch.pageX;
    y1 = y2 = touch.pageY;
});

如果你用兩根手指的話,那么event.touches.length就是2纲仍,如果是3根則為3呀袱,進(jìn)而分別獲得到每根手指的位置,由于我們是單點(diǎn)郑叠,所以就獲取第一個(gè)手指的位置即可夜赵。pageX/pageY是相當(dāng)于當(dāng)前html頁面的位置,而clientX和clientY是相對(duì)于視圖窗口的位置乡革。

然后在touchmove的時(shí)候獲取到最新的移動(dòng)位置:

dom.addEventListener("touchmove", function(event){
    var touch = event.touches[0];
    x2 = touch.pageX;
    y2 = touch.pageY;
});

最后touchend的時(shí)候寇僧,比較位移偏差:

dom.addEventListener("touchend", function(event){
    if(Math.abs(x2 - x1) < 10 && Math.abs(y2 - y1) < 10){
        $Element.dispatchEvent(dom, "tap", new $Event(x1, y1));
    }
    y2 = x2 = 0;
});

如果兩者的位移差小于10,則認(rèn)為是tap事件沸版,并觸發(fā)這個(gè)事件嘁傀。這里封裝了一個(gè)自定義事件:

class $Event{
    constructor(pageX, pageY){
        this.pageX = pageX;
        this.pageY = pageY;
    }   
}

然后就可以使用這個(gè)tap事件了,如下代碼所示:

$("#target").on("tap", function(event){
    console.log("tap", event.pageX, event.pageY);
});

接著在手機(jī)瀏覽器上運(yùn)行推穷,當(dāng)點(diǎn)擊目標(biāo)區(qū)域的時(shí)候就會(huì)執(zhí)行tap回調(diào)心包,而上下滑動(dòng)的時(shí)候則不會(huì)觸發(fā),如下圖所示:

再比較一下tap和原生click的觸發(fā)時(shí)間的差別馒铃,需要給自定義事件添加一個(gè)click:

dom.addEventListener("click", function(event){
    $Element.dispatchEvent(dom, "click", new $Event(event.pageX, event.pageY));
});

接著用一個(gè)tapTime記錄下時(shí)間:

var tapTime = 0;
$("div").on("tap", function(event){ 
    console.log("tap", event.pageX, event.pageY);
    tapTime = Date.now();
});

$("div").on("click", function(event){
    console.log("time diff", Date.now() - tapTime);
});

點(diǎn)擊后蟹腾,觀察控制臺(tái)的輸出:

click會(huì)大概慢20ms,可能是因?yàn)樗懊孢€要觸發(fā)mouse的事件区宇。

這樣我們就實(shí)現(xiàn)了一個(gè)自定義tap事件娃殖,是自己封裝了一個(gè)事件機(jī)制,fastclick是使用原生的Event议谷,如下fastclick的源碼炉爆,在touchend的回調(diào)函數(shù)里面執(zhí)行:

touch = event.changedTouches[0];

// Synthesise a click event, with an extra attribute so it can be tracked
clickEvent = document.createEvent('MouseEvents');
clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
clickEvent.forwardedTouchEvent = true; 
targetElement.dispatchEvent(clickEvent);

然后再調(diào)event.preventDefault禁掉原本的click事件的觸發(fā)。它里面還做了其它一些的兼容性的處理卧晓。

這個(gè)時(shí)候如果要做一個(gè)放大的事件芬首,你應(yīng)該不難想到實(shí)現(xiàn)的方法”岂桑可以在touchstart里面獲取event.touches兩根手指的初始位置郁稍,保存初始化手指的距離,然后在touchmove里面再次獲取新位置胜宇,計(jì)算新的距離減掉老的距離耀怜,如果是正數(shù)則說明是放大恢着,反之縮小,放大和縮小的尺度也是可以取到一個(gè)相對(duì)值财破。手機(jī)Safari有一個(gè)gesturestart/gesturechange/gestureend事件掰派,在gesturechange的event里面有一個(gè)放大比例scale的屬性。讀者可以自己嘗試實(shí)現(xiàn)一個(gè)放大和縮小的手勢事件左痢。

當(dāng)知道了怎么實(shí)現(xiàn)一個(gè)自定義事件之后靡羡,現(xiàn)在來實(shí)現(xiàn)一個(gè)更為復(fù)雜的“搖一搖”事件。

3. 搖一搖事件

html5新增了一個(gè)devicemotion的事件抖锥,可以使用手機(jī)的重力感應(yīng)亿眠。如下代碼所示:

window.ondevicemotion = function(event){
    var gravity = event.accelerationIncludingGravity;
    console.log(gravity.x, gravity.y, gravity.z);
}

x,y磅废,z表示三個(gè)方向的重力加速度,如下圖所示:

x是手機(jī)短邊荆烈,y是長邊拯勉,z是和手機(jī)屏幕垂直的方向,當(dāng)把手機(jī)平著放的時(shí)候憔购,由于x宫峦、y和地平線平行,所以g(x) = g(y) = 0玫鸟,而z和地平線垂直导绷,所以g(z) = 9.8左右,同理當(dāng)把手機(jī)豎著放的時(shí)候屎飘,g(x) = g(z) = 0妥曲,而g(y) = -9.8.

devicemotion事件會(huì)不斷地觸發(fā),而且觸發(fā)得很快钦购。

當(dāng)我們把手機(jī)拿起來搖一搖的時(shí)候檐盟,這個(gè)場景應(yīng)該是這樣的:

y軸和x軸的變化范圍從-45o到+45o,即這個(gè)區(qū)間是:

delta = 9.8 * sin(45o) * 2 = 13.8

即只要x軸和y軸的g值變化超過13.8押桃,我們就認(rèn)為發(fā)生了搖一搖事件葵萎。

根據(jù)上面的分析,不難寫出以下的代碼:

const EMPTY_VALUE = 100;
const THREAD_HOLD = 13.8;
var minX = EMPTY_VALUE,
    minY = EMPTY_VALUE;
window.ondevicemotion = function(event){
    var gravity = event.accelerationIncludingGravity,
        x = gravity.x,
        y = gravity.y;
    if(x < minX) minX = x;
    if(y < minY) minY = y;
    if(Math.abs(x - minX) > THREAD_HOLD &&  
            Math.abs(y - minY) > THREAD_HOLD){
        console.log("shake");
        var event = new CustomEvent("shake");
        window.dispatchEvent(event);
        minX = minY = EMPTY_VALUE;
    }   
}   

window.addEventListener("shake", function(){
    console.log("window shake callback was called");
});

用一個(gè)minX和minY記錄最小的值唱凯,每次devicemotion觸發(fā)的時(shí)候就判斷當(dāng)前的g值與最小值的差值是否超過了閾值羡忘,如果是的話就創(chuàng)建一個(gè)CustomEvent的實(shí)例,然后disatch給window磕昼,window上兼聽的onshake事件就會(huì)觸發(fā)了卷雕。

現(xiàn)在拿起手機(jī)搖一搖,控制臺(tái)就會(huì)輸出:

這樣就實(shí)現(xiàn)了一個(gè)搖一搖shake事件掰烟。還有一個(gè)問題就是:這個(gè)shake會(huì)不會(huì)很容易觸發(fā)爽蝴,即使不是搖一搖操作它也觸發(fā)了沐批?根據(jù)實(shí)驗(yàn)上面代碼如果不搖不容易觸發(fā)shake,同時(shí)搖的時(shí)候比較容易觸發(fā)蝎亚。如果太難觸發(fā)可以把閾值改小點(diǎn)九孩。
當(dāng)然判斷是否搖一搖的算法不止上面一個(gè),你還可以想出其它更好的方法发框。

綜上躺彬,本文討論了怎么去掉移動(dòng)端click事件遲鈍的300ms延遲,怎么實(shí)現(xiàn)一個(gè)快速響應(yīng)的tap事件梅惯,怎么封裝和觸發(fā)自定義事件宪拥,以及搖一搖的原理是怎么樣的,怎么實(shí)現(xiàn)一個(gè)搖一搖的shake事件铣减。
相信閱讀了本文她君,你就知道了怎么用一些基本事件進(jìn)行組合觸發(fā)一些高級(jí)事件。通常把這些基本事件封裝起來葫哗,如上面用一個(gè)$Element的類缔刹,由它負(fù)責(zé)決定是否觸發(fā)tap,而高層的調(diào)用者不需要關(guān)心tap事件觸發(fā)的細(xì)節(jié)劣针,這個(gè)$Element就相當(dāng)于一個(gè)事件代理校镐,或者也可以把tap當(dāng)作一個(gè)門面。所以它是一個(gè)代理模式或者門面模式捺典。更多設(shè)計(jì)模式可以查看本文《JS與面向?qū)ο?/a>》

原文:從移動(dòng)端click到搖一搖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鸟廓,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子襟己,更是在濱河造成了極大的恐慌引谜,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稀蟋,死亡現(xiàn)場離奇詭異煌张,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)退客,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門骏融,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人萌狂,你說我怎么就攤上這事档玻。” “怎么了茫藏?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵误趴,是天一觀的道長。 經(jīng)常有香客問我务傲,道長凉当,這世上最難降的妖魔是什么枣申? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮看杭,結(jié)果婚禮上忠藤,老公的妹妹穿的比我還像新娘。我一直安慰自己楼雹,他們只是感情好模孩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贮缅,像睡著了一般榨咐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谴供,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天块茁,我揣著相機(jī)與錄音,去河邊找鬼憔鬼。 笑死龟劲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的轴或。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼仰禀,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼照雁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起答恶,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤饺蚊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后悬嗓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體污呼,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年包竹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了燕酷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡周瞎,死狀恐怖苗缩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情声诸,我是刑警寧澤酱讶,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站彼乌,受9級(jí)特大地震影響泻肯,放射性物質(zhì)發(fā)生泄漏渊迁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一灶挟、第九天 我趴在偏房一處隱蔽的房頂上張望琉朽。 院中可真熱鬧,春花似錦膏萧、人聲如沸漓骚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝌蹂。三九已至,卻和暖如春曹锨,著一層夾襖步出監(jiān)牢的瞬間孤个,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工沛简, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留齐鲤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓椒楣,卻偏偏與公主長得像给郊,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子捧灰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容