Tween Animation原理


補(bǔ)間動(dòng)畫挫望,設(shè)置動(dòng)畫初始與結(jié)束狀態(tài)立润,中間狀態(tài)由系統(tǒng)計(jì)算并控制。Animation是抽象類媳板,它的子類實(shí)現(xiàn)動(dòng)畫的具體行為和效果桑腮,動(dòng)畫幀的顯示與視圖關(guān)聯(lián)。四種補(bǔ)間動(dòng)畫類型蛉幸,平移破讨,旋轉(zhuǎn)丛晦,透明度,縮放提陶。

示例介紹

Animation mAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.tween_anim_sample);
mAnimation.setFillAfter(true);//動(dòng)畫結(jié)束后保留結(jié)束狀態(tài)
mAnimation.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
    }
    @Override
    public void onAnimationEnd(Animation animation) {
    }
    @Override
    public void onAnimationRepeat(Animation animation) {
    }
});
mTextView.startAnimation(mAnimation);
//xml定義烫沙,以縮放為例
<scale
    android:duration="6000"
    android:fromXScale="1.0"
    android:fromYScale="1.0"
    android:pivotX="50%"
    android:pivotY="50%"http://縮放的中心點(diǎn),視圖中點(diǎn)
    android:toXScale="0.2"
    android:toYScale="0.2" />

實(shí)現(xiàn)一個(gè)補(bǔ)間動(dòng)畫隙笆,過程很簡單锌蓄,兩步就可以。首先創(chuàng)建一個(gè)動(dòng)畫對象撑柔,可以在xml中定義瘸爽。然后,對將要進(jìn)行動(dòng)畫的視圖執(zhí)行startAnimation方法乏冀,另外蝶糯,可以設(shè)置動(dòng)畫監(jiān)聽。下面分析一下原理辆沦,動(dòng)畫從啟動(dòng)到結(jié)束昼捍,系統(tǒng)是如何控制的。


動(dòng)畫原理

補(bǔ)間動(dòng)畫基本流程

每個(gè)視圖都可以實(shí)現(xiàn)補(bǔ)間動(dòng)畫肢扯,在基類View中定義動(dòng)畫的啟動(dòng)方法妒茬。

public void startAnimation(Animation animation) {
    animation.setStartTime(Animation.START_ON_FIRST_FRAME);//值-1
    setAnimation(animation);//為View設(shè)置動(dòng)畫
    invalidateParentCaches();
    invalidate(true);//重繪
}

設(shè)置Animation內(nèi)部mStartTime值,(值-1)蔚晨,初始化mStarted和mEnd標(biāo)志乍钻。視圖內(nèi)部mCurrentAnimation賦值,并觸發(fā)動(dòng)畫#reset方法铭腕,動(dòng)畫初始化狀態(tài)重置银择。

protected void invalidateParentCaches() {
    if (mParent instanceof View) {
        ((View) mParent).mPrivateFlags |= PFLAG_INVALIDATED;
    }
}

在視圖中,找到它的父視圖mParent累舷,為父視圖增加PFLAG_INVALIDATED標(biāo)志位浩考。在后面的invalidate方法時(shí),父視圖的Canvas將會(huì)重建被盈。
invalidate方法析孽,視圖重繪,

//invalidateInternal方法代碼只怎。
if (invalidateCache) {
    mPrivateFlags |= PFLAG_INVALIDATED;
    mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}

動(dòng)畫視圖增加PFLAG_INVALIDATED標(biāo)志袜瞬。
當(dāng)一個(gè)視圖執(zhí)行invalidate方法,硬件渲染時(shí)身堡,并非整個(gè)樹結(jié)構(gòu)視圖的Canvas全部重建邓尤。從根視圖的updateDisplayListIfDirty方法開始,在樹結(jié)構(gòu)視圖遍歷,每個(gè)節(jié)點(diǎn)都會(huì)執(zhí)行該方法裁赠。

//View的updateDisplayListIfDirty方法代碼殿漠。
if (renderNode.isValid()&& !mRecreateDisplayList) {
    mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
    dispatchGetDisplayList();//派發(fā)子視圖
    return renderNode; // no work needed
}

若視圖Canvas不需要重建,觸發(fā)dispatchGetDisplayList方法佩捞。

private void recreateChildDisplayList(View child) {
    child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;
    child.mPrivateFlags &= ~PFLAG_INVALIDATED;
    child.updateDisplayListIfDirty();
    child.mRecreateDisplayList = false;
}

當(dāng)動(dòng)畫視圖的父視圖執(zhí)行到recreateChildDisplayList方法時(shí)绞幌,它曾設(shè)置過PFLAG_INVALIDATED標(biāo)志,因此一忱,動(dòng)畫啟動(dòng)后莲蜘,動(dòng)畫視圖與父視圖重建Canvas,它的上層視圖與動(dòng)畫視圖的兄弟視圖均不需要重建帘营。第一幀動(dòng)畫重建Canvas票渠,然后除去該標(biāo)志,后續(xù)動(dòng)畫不需要重建芬迄。

在父視圖的View#updateDisplayListIfDirty方法问顷,Canvas重建,然后禀梳,父視圖跳過繪制杜窄,它自己的onDraw方法不會(huì)觸發(fā),dispatchDraw方法分發(fā)繪制子視圖算途,也就是動(dòng)畫視圖塞耕。

...
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
    dispatchDraw(canvas);
    ...
} else {
    draw(canvas);
}

ViewGroup#dispatchDraw方法繪制子視圖,包括動(dòng)畫視圖與其兄弟視圖嘴瓤,觸發(fā)重載的帶三個(gè)參數(shù)的draw方法扫外,該方法將處理動(dòng)畫事務(wù)。該方法是動(dòng)畫視圖執(zhí)行廓脆。

boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
    ...
    final Animation a = getAnimation();
    if (a != null) {//處理動(dòng)畫狀態(tài)
        more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
        concatMatrix = a.willChangeTransformationMatrix();
        if (concatMatrix) {
            mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
        }
        transformToApply = parent.getChildTransformation();
    } 
    ...
    if (hardwareAcceleratedCanvas) {
        mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0;
        mPrivateFlags &= ~PFLAG_INVALIDATED;//除去PFLAG_INVALIDATED標(biāo)志
    }
    ...
    if (drawingWithRenderNode) {
        renderNode = updateDisplayListIfDirty();
    }
}

首先筛谚,判斷該視圖是否有動(dòng)畫對象,在動(dòng)畫狀態(tài)下停忿,處理視圖上活動(dòng)的動(dòng)畫驾讲,
觸發(fā)View#applyLegacyAnimation方法。第一幀動(dòng)畫視圖重建Canvas瞎嬉,執(zhí)行onDraw方法,然后厚柳,將除去PFLAG_INVALIDATED標(biāo)志氧枣,動(dòng)畫視圖后續(xù)將不再Canvas重建。

private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
                                     Animation a, boolean scalingRequired) {
    Transformation invalidationTransform;
    final int flags = parent.mGroupFlags;
    final boolean initialized = a.isInitialized();
    if (!initialized) {
        //Animation初始化
        a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight());
        a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);
        ...
        //mPrivateFlags加上PFLAG_ANIMATION_STARTED標(biāo)志
        onAnimationStart();
    }
    //父視圖內(nèi)部Transformation
    final Transformation t = parent.getChildTransformation();
    boolean more = a.getTransformation(drawingTime, t, 1f);
    ...
    if (more) {//成功
        if (!a.willChangeBounds()) {//動(dòng)畫不會(huì)改變View的邊界别垮,如Alpha動(dòng)畫
            if ((flags & (ViewGroup.FLAG_OPTIMIZE_INVALIDATE | 
                    ViewGroup.FLAG_ANIMATION_DONE)) ==  
                    ViewGroup.FLAG_OPTIMIZE_INVALIDATE) {
                parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED;
            } else if ((flags & ViewGroup.FLAG_INVALIDATE_REQUIRED) == 0) {
                parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
                //不改變邊界便监,繪制動(dòng)畫View的區(qū)域
                parent.invalidate(mLeft, mTop, mRight, mBottom);
            }
        } else {//動(dòng)畫會(huì)改變View的邊界,如Scale動(dòng)畫
            if (parent.mInvalidateRegion == null) {
                parent.mInvalidateRegion = new RectF();
            }
            //mInvalidateRegion改變區(qū)域
            final RectF region = parent.mInvalidateRegion;
            //該方法的目的就是改變mInvalidateRegion。
            a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
                    invalidationTransform);
            parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
            //region是動(dòng)畫過程中的改變區(qū)域
            final int left = mLeft + (int) region.left;
            final int top = mTop + (int) region.top;
            parent.invalidate(left, top, left + (int) (region.width() + .5f),
                    top + (int) (region.height() + .5f));
        }
    }
    return more;
}

Animation初始化烧董,入?yún)⑹莿?dòng)畫視圖寬高和父視圖的寬高(貌似沒用到)毁靶。
Animation#initializeInvalidateRegion方法將動(dòng)畫視圖區(qū)域存儲(chǔ)在內(nèi)部的mPreviousRegion對象,動(dòng)畫視圖增加PFLAG_ANIMATION_STARTED標(biāo)志逊移。
獲取父視圖內(nèi)部mChildTransformation预吆,將視圖改變存儲(chǔ)在Transformation的Matrix。
當(dāng)動(dòng)畫未結(jié)束胳泉,會(huì)繼續(xù)觸發(fā)父視圖#invalidate(區(qū)域)方法重繪拐叉。繪制區(qū)域包括兩種情況,一種是動(dòng)畫未改變視圖邊界扇商,如透明度動(dòng)畫凤瘦,另一種是改變視圖邊界,如Scale動(dòng)畫案铺,這兩種情況繪制區(qū)域不同蔬芥。但每一幀動(dòng)畫父視圖都會(huì)Canvas重建。
willChangeBounds方法控汉,判斷邊界是否改變笔诵,默認(rèn)改變邊界。例如暇番,縮放動(dòng)畫ScaleAnimation會(huì)改變邊界嗤放,透明度動(dòng)畫AlphaAnimation不會(huì)改變邊界,需重寫willChangeBounds方法壁酬。
若不改變視圖邊界次酌,繪制區(qū)域是動(dòng)畫視圖相對于父視圖坐標(biāo)系的邊界坐標(biāo)(mLeft, mTop, mRight, mBottom)。
若改變視圖邊界舆乔,子視圖相對父視圖的邊界距離不會(huì)改變(mLeft/mTop)岳服。子視圖寬高不會(huì)改變(getHeight與getWidth獲取)。
getInvalidateRegion方法希俩,根據(jù)視圖區(qū)域和Transformation變換獲取當(dāng)前需要改變的區(qū)域吊宋,在父視圖中存儲(chǔ)。改變的是mInvalidateRegion區(qū)域颜武。
動(dòng)畫視圖在父視圖中繪制圖璃搜。

動(dòng)畫繪制圖.png
黃色區(qū)域是動(dòng)畫視圖在父視圖中的位置,當(dāng)執(zhí)行放大動(dòng)畫時(shí)鳞上,逐漸動(dòng)畫擴(kuò)大成綠色區(qū)域(變化)这吻,但mLeft和mTop的值不會(huì)改變,改變的是mInvalidateRegion區(qū)域篙议,子視圖相對父視圖的邊界mLeft/mTop(固定)加上mInvalidateRegion即綠色區(qū)域唾糯。從圖中例子可以看出怠硼,放大動(dòng)畫的改變區(qū)域left與top都是負(fù)值,使得綠色區(qū)域相對父視圖邊界在變小移怯,最終觸發(fā)父視圖#invalidate(綠色區(qū)域)繪制香璃,實(shí)現(xiàn)動(dòng)畫。動(dòng)畫未結(jié)束會(huì)一直在繪制舟误。


動(dòng)畫改變分析

public boolean getTransformation(long currentTime, Transformation outTransformation) {
    //若開始時(shí)間為-1葡秒,說明動(dòng)畫剛開始,設(shè)置開始事件為View繪制的當(dāng)前時(shí)間
    if (mStartTime == -1) {
        mStartTime = currentTime;
    }
    final long startOffset = getStartOffset();
    final long duration = mDuration;
    float normalizedTime;
    if (duration != 0) { //經(jīng)歷的時(shí)間占總耗時(shí)的比例
        normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /(float) duration;
    } else {
        normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
    }
    //比例大于等于1說明動(dòng)畫結(jié)束
    final boolean expired = normalizedTime >= 1.0f;
    mMore = !expired;
    ...
    if ((normalizedTime >= 0.0f || mFillBefore) &&
                    (normalizedTime <= 1.0f || mFillAfter)) {
        if (!mStarted) {
            fireAnimationStart();
            mStarted = true;
        }
        ...
        final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
        applyTransformation(interpolatedTime, outTransformation);
    }
    if (expired) {//動(dòng)畫結(jié)束脐帝,判斷重復(fù)
        if (mRepeatCount == mRepeated) {
          
        } else {//重復(fù)動(dòng)畫
            if (mRepeatCount > 0) {
                mRepeated++;
            }
            ...
        }
    }
    ...
    return mMore;
}

首先同云,根據(jù)當(dāng)前時(shí)間、開始時(shí)間和動(dòng)畫持續(xù)時(shí)間堵腹,計(jì)算動(dòng)畫已完成比例炸站,判斷動(dòng)畫完成,若比例>=1疚顷,說明動(dòng)畫執(zhí)行已到達(dá)持續(xù)時(shí)間旱易,expired失效,返回結(jié)束標(biāo)志腿堤。
normalizedTime時(shí)間占比是動(dòng)畫從mStartTime(設(shè)置的開始時(shí)間)開始計(jì)算阀坏,已運(yùn)行時(shí)間占總時(shí)間的比例。
其次笆檀,在動(dòng)畫運(yùn)行過程中忌堂,觸發(fā)applyTransformation方法,將視圖變化寫入Transformation內(nèi)部Matrix酗洒,它是一個(gè)抽象方法士修,Animation的子類去實(shí)現(xiàn)不同類型動(dòng)畫。插值器Interpolator通過控制時(shí)間占比來計(jì)算動(dòng)畫運(yùn)動(dòng)的變化率樱衷。
最后棋嘲,在動(dòng)畫開始和結(jié)束時(shí),根據(jù)mStarted標(biāo)志和重復(fù)標(biāo)志矩桂,fireAnimationStart方法和fireAnimationEnd方法沸移,調(diào)用動(dòng)畫AnimationListener監(jiān)聽器。若動(dòng)畫重復(fù)侄榴,自增mRepeated雹锣,mStartTime設(shè)值-1,重新計(jì)時(shí)癞蚕。
下面以子類ScaleAnimation為例蕊爵,分析實(shí)現(xiàn)動(dòng)畫改變的applyTransformation方法。

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    float sx = 1.0f;
    float sy = 1.0f;
    //獲取Scale因子涣达,在Anmiation類中在辆。
    float scale = getScaleFactor();
    if (mFromX != 1.0f || mToX != 1.0f) {
        sx = mFromX + ((mToX - mFromX) * interpolatedTime);
    }
    if (mFromY != 1.0f || mToY != 1.0f) {
        sy = mFromY + ((mToY - mFromY) * interpolatedTime);
    }
    if (mPivotX == 0 && mPivotY == 0) {
        t.getMatrix().setScale(sx, sy);
    } else {
        t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
    }
}

視圖在x軸Scale的初始比例mFromX,目標(biāo)比例mToX度苔,當(dāng)值是1.0f是正常視圖匆篓。若mFromX和mToX有一個(gè)不是1.0f,說明動(dòng)畫正在進(jìn)行寇窑,發(fā)生變化鸦概。
sx是當(dāng)前x軸Scale比例值,(mToX-mFromX)是Scale變化差值甩骏,interpolatedTime是根據(jù)Interpolation計(jì)算動(dòng)畫運(yùn)行進(jìn)度占比窗市,實(shí)現(xiàn)動(dòng)畫速度控制。根據(jù)interpolatedTime饮笛、開始值(mFromX)和結(jié)束值(mToX)咨察,計(jì)算當(dāng)前sx和sy。
Matrix#setScale方法福青,將sx和sy保存在底層Matrix摄狱。mPivotX與mPivotY是Scale的中心點(diǎn),默認(rèn)值是視圖左上角坐標(biāo)(0,0)无午,中心點(diǎn)不在左上角時(shí)媒役,將mPivotX和mPivotY一起保存。
保存Transformation后宪迟,getTransformation方法將返回是否動(dòng)畫的標(biāo)志酣衷,接下來繼續(xù)回到applyLegacyAnimation方法,根據(jù)運(yùn)行標(biāo)志次泽,計(jì)算刷新邊界穿仪,刷新父視圖。下面看一下getInvalidateRegion方法箕憾,計(jì)算改變的區(qū)域牡借。

public void getInvalidateRegion(int left, int top, int right, int bottom,
            RectF invalidate, Transformation transformation) {
    final RectF tempRegion = mRegion;
    final RectF previousRegion = mPreviousRegion;
    //先設(shè)置為視圖View區(qū)域(以View坐標(biāo))
    invalidate.set(left, top, right, bottom);
    //底層變換
    transformation.getMatrix().mapRect(invalidate);
    invalidate.inset(-1.0f, -1.0f);
    //invalidate區(qū)域值設(shè)置成內(nèi)部mRegion
    tempRegion.set(invalidate);
    //與上次變換的得到的區(qū)域合并
    invalidate.union(previousRegion);
    //存儲(chǔ)變換后的區(qū)域
    previousRegion.set(tempRegion);

    final Transformation tempTransformation = mTransformation;
    final Transformation previousTransformation = mPreviousTransformation;

    tempTransformation.set(transformation);
    transformation.set(previousTransformation);
    //存儲(chǔ)此次變換
    previousTransformation.set(tempTransformation);
}

入?yún)⑹莿?dòng)畫視圖區(qū)域,以視圖自己坐標(biāo)系為標(biāo)準(zhǔn)坐標(biāo)值(0,0,width,height)袭异, RectF區(qū)域invalidate在父視圖保存钠龙,它是上一次動(dòng)畫視圖幀的改變區(qū)域(變換+合并),將它重新傳入御铃,計(jì)算這次動(dòng)畫幀的改變區(qū)域碴里。

首先,將invalidate設(shè)置成動(dòng)畫視圖區(qū)域(動(dòng)畫視圖坐標(biāo)系)上真,調(diào)用Matrix的JNI#native_mapRect(native_instance, dst, src)方法咬腋,底層mapRect方法的Matrix變換,將一個(gè)src區(qū)域(0,0,width,height)轉(zhuǎn)換為一個(gè)新的dst目標(biāo)區(qū)域睡互。源區(qū)域src和目標(biāo)區(qū)域都是invalidate根竿。因此陵像,轉(zhuǎn)換后的區(qū)域保存在invalidate。根據(jù)動(dòng)畫運(yùn)行時(shí)間寇壳,Matrix轉(zhuǎn)換程度不同醒颖。
然后,變換后的目標(biāo)區(qū)域invalidate與之前保存mPreviousRegion區(qū)域合并壳炎,mPreviousRegion默認(rèn)初始化動(dòng)畫視圖區(qū)域泞歉,此后,在動(dòng)畫過程中匿辩,專門存儲(chǔ)每次變換后的區(qū)域腰耙。tempRegion緩存新dst目標(biāo)區(qū)域,賦值給previousRegion铲球,下一次變換后合并時(shí)使用挺庞。
最終,invalidate設(shè)置的值是以(0,0,width,height)區(qū)域進(jìn)行變換稼病,再+合并的改變區(qū)域挠阁。

改變區(qū)域的本質(zhì)是動(dòng)畫視圖在以自己為坐標(biāo)系的坐標(biāo)值(0,0,width,height)區(qū)域中,根據(jù)當(dāng)前動(dòng)畫進(jìn)度計(jì)算的Matrix溯饵,轉(zhuǎn)換成一個(gè)新的坐標(biāo)區(qū)域侵俗。
以Scale動(dòng)畫放大視圖為例,若中心點(diǎn)是動(dòng)畫視圖的中心(不是以左上角為中心)丰刊,則得到的目標(biāo)區(qū)域left/top是負(fù)值隘谣。新dst目標(biāo)區(qū)域(-5,-5,width+5,height+5),這個(gè)是以動(dòng)畫視圖自己坐標(biāo)系的坐標(biāo)值啄巧。
父視圖繪制的區(qū)域寻歧,invalidate(區(qū)域)需要相對父視圖的坐標(biāo)系的坐標(biāo)值,mLeft/mTop+新dst目標(biāo)區(qū)域秩仆,即是動(dòng)畫當(dāng)前的繪制區(qū)域(示意圖綠色區(qū)域)码泛。


總結(jié)

補(bǔ)間動(dòng)畫的核心本質(zhì)是在一定的持續(xù)時(shí)間內(nèi),不斷改變視圖Matrix變換澄耍,并且不斷刷新的過程噪珊。

在動(dòng)畫過程中,第一幀刷新父視圖和動(dòng)畫視圖齐莲,后面僅刷新父視圖痢站,刷新區(qū)域通過計(jì)算獲取,invalidate(區(qū)域)选酗,父視圖每一幀都會(huì)Canvas重建阵难。
在動(dòng)畫啟動(dòng)時(shí),第一幀動(dòng)畫視圖Canvas重建芒填,后續(xù)動(dòng)畫幀呜叫,動(dòng)畫視圖不會(huì)再Canvas重建空繁。
動(dòng)畫視圖的兄弟視圖不會(huì)Canvas重建,不會(huì)觸發(fā)onDraw方法朱庆。


任重而道遠(yuǎn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末家厌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子椎工,更是在濱河造成了極大的恐慌,老刑警劉巖蜀踏,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件维蒙,死亡現(xiàn)場離奇詭異,居然都是意外死亡果覆,警方通過查閱死者的電腦和手機(jī)颅痊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來局待,“玉大人斑响,你說我怎么就攤上這事∏ィ” “怎么了舰罚?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長薛耻。 經(jīng)常有香客問我营罢,道長,這世上最難降的妖魔是什么饼齿? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任饲漾,我火速辦了婚禮,結(jié)果婚禮上缕溉,老公的妹妹穿的比我還像新娘考传。我一直安慰自己,他們只是感情好证鸥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布僚楞。 她就那樣靜靜地躺著,像睡著了一般枉层。 火紅的嫁衣襯著肌膚如雪镜硕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天返干,我揣著相機(jī)與錄音兴枯,去河邊找鬼。 笑死矩欠,一個(gè)胖子當(dāng)著我的面吹牛财剖,可吹牛的內(nèi)容都是我干的悠夯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼躺坟,長吁一口氣:“原來是場噩夢啊……” “哼沦补!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咪橙,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤夕膀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后美侦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體产舞,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年菠剩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了易猫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡具壮,死狀恐怖准颓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棺妓,我是刑警寧澤攘已,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站怜跑,受9級特大地震影響贯被,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妆艘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一彤灶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧批旺,春花似錦幌陕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至暇赤,卻和暖如春心例,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鞋囊。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工止后, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓译株,卻偏偏與公主長得像瓜喇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子歉糜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

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