1. 程式人生 > >仿炫酷頭條小視訊拖拽動畫

仿炫酷頭條小視訊拖拽動畫

本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出

序言

時光荏苒,又是一年高考季,不知不覺中距離高考已經七年過去了,在我眼中依稀能夠嗅到那個夏天的味道,朋友的道別,得知成績後的苦澀,獨自靜聽大海的翻湧。。。

歲月安好,在浮躁的年紀裡,我撬動鍵盤記錄回憶

大哥們,請放下刀,MD 我只想說,我老的真快。

正文

今天想跟大家分享的是頭條小視訊拖拽的動畫效果,玩過頭條的小夥伴肯定感受過。酷酷的效果:

demo.gif

由於完整效果圖體積太大,只展示了一部分,想體驗完整的效果請連結以下地址:

深有體會,寫好一個控制元件,重點在於觀察分析,化繁為簡,經常會進入死衚衕,換一換思路,將柳暗花明。

大哥快放下你的刀,小弟不BB了。

觀察分析頭條動畫

開啟手機 -> 設定 -> 開發者選項 -> 選擇視窗動畫縮放 程式動畫時長(5x) -> 開啟頭條進入小視訊列表操作觀察。大概可以分為以下3部分:

  1. 列表頁進入到詳情頁的過渡動畫
  2. 詳情頁拖動動畫
  3. 釋放後回到列表頁的過渡動畫

接下來逐一講解

列表頁進入到詳情頁的過渡動畫

過渡動畫的實現有兩種方式,第一種是共享元素 ActivityOptionsCompat 使用簡單,缺點是隻相容 5.0 以上;第二種方式手動實現過渡動畫,可控性強,相容 5.0 以下版本,實現比較複雜。最開始我是通過共享元素去實現的過渡動畫,但效果差強人意,只好老老實實的手動去實現過渡動畫。手動實現過渡動畫感觸可以讓我引用美國一名大演說家的名言 “細節決定成敗” ,那麼讓我們一起來看看有哪些細節需要處理。

通過分析,可以得出列表頁 -> 詳情頁可以拆分為平移 + 縮放動畫,列表頁的封面圖相對螢幕的位置開始縮放平移鋪滿螢幕。那麼首先就需要考慮的是動畫是在列表頁實現,監聽動畫結束跳轉到詳情頁;還是直接跳轉到詳情頁,由詳情頁來實現動畫。這兩種方案都可以,我通過觀察頭條採用的是後一種方案,以下是觀察的結果:

vdr_3.png

點選 item ,截圖如下:

vdr_2.png

可以觀察到執行動畫的同時已經跳轉到了詳情頁,並且我這裡選擇的是 動畫程式時長縮放 ,也再一次證明了頭條也是採用手動實現過渡動畫的方式,不幸的是讓我再一次發現了頭條的一個小小 bug ,手指釋放後執行過渡動畫期間可以再次拖動。言歸正傳,最終確定在詳情頁實現過渡動畫。

過渡動畫流程簡介:

點選列表頁封面圖 -> 啟動詳情頁,在佈局最外層鋪一張同列表頁相同的封面圖(全屏),並縮放平移到列表封面圖的相同大小相同位置 -> 封面圖平移放大至全屏 -> 動畫結束隱藏封面圖並載入視訊資源。(也可以把封面圖當做視訊的第一幀處理)有點狸貓換太子的感覺,真真假假,分不清楚。

過渡動畫最終被拆分成:

  1. 平移動畫
  2. 縮放動畫

繼續拆分

  1. x軸方向平移動畫
  2. y軸方向平移動畫
  3. x軸方向縮放動畫
  4. y軸方向縮放動畫

這裡以x軸方向平移動畫為例,我們需要x軸方向平移的 起始值結束值起始值 為列表封面圖左頂點, 結束值 為螢幕原點。可以通過以下方法獲取:

View.getGlobalVisibleRect(Rect r)

x軸方向平移動畫:

float value = (float) animation.getAnimatedValue();
setTranslationX(r.x- value * r.x);

其他幾種動畫我這裡就不在一 一累述,如有不明白的請查閱原始碼以及留言諮詢。

接著看以下這種情況:

如果列表封面圖的 item 未完全展示,有部分滑出螢幕頂部(這裡稱作上邊界越界),同理 item 有部分未滑入螢幕底部(上邊界越界),點選 item 會出現一種什麼樣的情況,以下是頭條的截圖:

vdr_4.png

明顯的被壓縮變形了,降低了使用者體驗度,那麼怎麼優化呢?

//`item` 的實際高度替換可見高度。
startScaleY = (float) item.getHeight()/ screenHeight; 

優化後的效果圖如下:

vdr_6.png

頭條很巧妙的避免了從詳情頁 -> 列表頁引起的變形問題。採用的方案是,移動了列表 item 的位置使之完全可見。

最後的需要實現列表頁無縫過渡到詳情頁,讓使用者感覺不到有介面的跳動,先去除系統自帶的轉場效果:

 overridePendingTransition(0, 0);

並把詳情頁 Activity 設定成透明,主題新增如下程式碼:

<item name="android:windowIsTranslucent">true</item>

然後設定列表頁封面圖以及詳情頁大圖縮放型別為:

android:scaleType="centerCrop"

在最初的實現當中我忽略了一個很重要的環節,我使用的測試機解析度為 1080*1920 ,並把列表頁 item 的寬高比設定為 9 : 16 ,碰巧使詳情頁大圖跟列表頁封面圖縮放比例一致從而實現了無縫的過渡。後來測試的小哥哥跑過來告訴我在他手機上過渡有明顯的抖動,不符合預期。尼瑪,怎麼會?一跑,瞪大雙眼,還真會,這 … 我的鍋。

開啟除錯模式 … 咦 . . 過渡動畫並沒有問題 . . 螢幕比例 . . 圖片縮放比例 . . 哈哈,我知道問題所在了,列表封面圖的寬高比必須和詳情頁大圖(全屏)的寬高比一致,不然會導致 centerCrop 的縮放比例不一致,引起的抖動問題。那麼動態設定封面圖圖片寬高比:

ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone((ConstraintLayout) helper.itemView);
constraintSet.setDimensionRatio(R.id.iv_bg, "H," + DensityUtil.getScreenSize(mContext).x + ":" + DensityUtil.getScreenSize(mContext).y);
constraintSet.applyTo((ConstraintLayout) helper.itemView);

第1部分的過渡動畫到此差不多結束了

詳情頁拖動動畫

觀察頭條詳情頁拖動動畫具有以下特點:

  • x軸平移
  • 縮放,縮放中心點為螢幕底部中點
  • 釋放動畫,y軸偏移量大於臨界值(螢幕高度的十分之一)釋放手指執行過渡動畫,否則執行恢復動畫
  • 拖動監聽,拖動過程中隱藏非視訊控制元件
  • 消費事件,預設最外層父控制元件消費事件,如何把事件傳遞給子控制元件消費
  • 滑動衝突,左右滑動時與 viewpager 造成的滑動衝突

x軸平移

x 軸的平移動畫是相對簡單的,根據 x 軸的偏移量來設定平移距離:

setTranslationX(getTranslationX() + dx);

縮放

縮放動畫是由 y 軸偏移量決定的,當 y 軸偏移量大於臨界值(螢幕高度的二分之一)時,則執行 y 軸方向的平移動畫:

//設定縮放中心點
setPivotX(getWidth() / 2F);
setPivotY(getHeight());

float scale = 1.0F - dy / getHeight();
//縮放動畫 
setScaleX(scale);
setScaleY(scale);
//平移
if (scale < mStartOffsetRatioY) { // 0.5 
    setTranslationY(getTranslationY() + dy / 2);
}

釋放動畫

根據 y 軸偏移量大於臨界值(螢幕高度的十分之一)分別執行回退的過渡動畫和恢復動畫

final boolean isEnd = ((dy / getHeight()) > 0.1);
if (isEnd) {
    //執行回退的過渡動畫
    startEndAnimation();
} else {
    //執行恢復動畫
    startRestorationAnimation();
}

恢復動畫的程式碼如下:

PropertyValuesHolder propertyScaleX = PropertyValuesHolder.ofFloat("scaleX", getScaleX(), 1.0F);
PropertyValuesHolder propertyScaleY = PropertyValuesHolder.ofFloat("scaleY", getScaleY(), 1.0F);
PropertyValuesHolder propertyTranslationX = PropertyValuesHolder.ofFloat("translationX", getTranslationX(), 0);
PropertyValuesHolder propertyTranslationY = PropertyValuesHolder.ofFloat("translationY", getTranslationY(), 0);
animation = ObjectAnimator.ofPropertyValuesHolder(this, propertyScaleX, propertyScaleY, propertyTranslationX, propertyTranslationY).setDuration(400);
//恢復動畫開始
animation.start();

回退的過渡動畫部分程式碼如下(請結合原始碼理解):

float value = (float) animation.getAnimatedValue();
setScaleX(startScaleX + value * (endScaleX - startScaleX));
setScaleY(startScaleY + value * (endScaleY - startScaleY));
setTranslationX(startTransitionX + value * (mOriginViewX - startTransitionX) - value * (getWidth() - mOriginViewVisibleWidth) / 2.0F);

setTranslationY(startTransitionY - value * (startTransitionY - mOriginViewY) - value * (getHeight() - mOriginViewRealHeight));

由於拖拽時設定的縮放中心點為螢幕底部中點,所以 x 方向的平移需要減去 (getWidth() - mOriginViewVisibleWidth) / 2.0F) ,同理 y 方向的平移也需要減去 getHeight() - mOriginViewRealHeight

拖動監聽

so easy 直接貼程式碼:

case MotionEvent.ACTION_MOVE:
 if (mListener != null) {
    mListener.onStartDrag();
 }

消費事件

頭條詳情頁既可以拖動又可以響應 OnClickListener 事件,細細琢磨你會發現,最外層的父控制元件需要攔截並消費 touch 事件,攔截了事件傳遞,那麼子控制元件又怎麼響應 OnClickListener 事件。有以下的三種處理方案:

  1. 父控制元件不攔截事件,只消費事件。(如果子控制元件有攔截事件,那麼就不會走父控制元件的 touch 事件,導致無拖動效果,適合子控制元件無攔截事件)
  2. 父控制元件根據特定標識判定是否攔截事件。(如 setTag 的方式)
  3. 在控制元件 dispatchTouchEvent 分發事件裡判定是否有滑動的趨勢來判定是否攔截事件

    第一種方案直接被 pass 掉,第二種方案根據當前觸控的子 View 是否有特定的 tag 標記來決定是否攔截事件。那麼怎麼來獲取觸控的 View 是否有 tag 標記呢?採用的是比較常規的處理方式,遍歷整個父控制元件的子控制元件(樹形結構),觸控點是否包含在子控制元件內同時獲取 tag 資訊,相關的程式碼如下:

     private boolean childInterceptEvent(ViewGroup parentView, int touchX, int touchY) {
        boolean isConsume = false;
        for (int i = parentView.getChildCount() - 1; i >= 0; i--) {
            View childView = parentView.getChildAt(i);
            if (!childView.isShown()) {
                continue;
            }
            boolean isTouchView = isTouchView(touchX, touchY, childView);
            if (isTouchView && childView.getTag() != null && TAG_DISPATCH.equals(childView.getTag().toString())) {
                isConsume = true;
                break;
            }
            if (childView instanceof ViewGroup) {
                ViewGroup itemView = (ViewGroup) childView;
                if (!isTouchView) {
                    continue;
                } else {
                    //遞迴
                    isConsume |= childInterceptEvent(itemView, touchX, touchY);
                    if (isConsume) {
                        break;
                    }
                }
            }
        }
        return isConsume;
    }

    view 是否包含觸控點:

     private boolean isTouchView(int touchX, int touchY, View view) {
        Rect rect = new Rect();
        view.getGlobalVisibleRect(rect);
        return rect.contains(touchX, touchY);
    }

    父控制元件攔截事件程式碼如下:

     @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                return !childInterceptEvent(this, (int) ev.getRawX(), (int) ev.getRawY());
        }
        return super.onInterceptTouchEvent(ev);
    }

    那麼只需要在佈局檔案中新增以下一行程式碼就可以響應 OnClickListener 事件:

    android:tag="dispatch"

聰明如斯的你肯定會發現設定 tag 是一件多麼操蛋的事,如果沒有事先約定誰知道要設定 tag 以及 tag 的值,還有就是如果子控制元件是第三方控制元件,那麼還得在程式碼中手動設定。這樣就衍生出了第三方案,根據滑動的趨勢來判定是否攔截事件,相關的程式碼如下:

     @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        float y = ev.getRawY();
        float x = ev.getRawX();
        switch (ev.getAction() & ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mTouchLastX = x;
                mTouchLastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                float dy = y - mTouchLastY;
                float dx = x - mTouchLastX;
                //判定是否攔截事件
                mIsInterceptTouchEvent = Math.abs(dy) > mMinScaledTouchSlop | Math.abs(dx) > mMinScaledTouchSlop;
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

最終採用的是 方案二 + 方案三,採用方案二是為了相容舊版本。

滑動衝突

有關滑動衝突的解決方案可以查閱以下文章:

當前需要解決與 viewpager 左右滑動衝突,處理的策略是:y 軸的偏移量是否大於 x 軸偏移量來決定 touch 事件的消費。核心程式碼如下:

if (Math.abs(dx) >= Math.abs(dy)) {
//消費事件
}else{
//不消費事件
}

到這裡拖拽就有一個大體的效果了,有一個小小的細節,頭條拖拽到頂部有明顯的卡頓,本文的方式並不會卡頓,絲滑如初。剩下第三部分回退的過渡動畫。

釋放後回到列表頁的過渡動畫

還是老架勢,分析頭條的效果:

  • 列表頁 item 完全可見,回退的過渡動畫將縮放平移至列表 item 相同大小,相同位置
  • 列表頁 item 越界(上越界或下越界),回退的過渡動畫將縮放平移至列表頂部,如果列表最後兩條資料下越界則縮放平移至列表底部。

首先需要判定是否越界:

//+1精度誤差
if ((mOriginViewVisibleHeight + 1) < mOriginViewRealHeight) {
    if ((mOriginViewY + mOriginViewRealHeight) > getHeight()) {
        //下邊界越界
    } else {
        //上邊界越界
    }
}

mOriginViewVisibleHeight 列表 item 可見高度,mOriginViewRealHeight 列表 item 的真實高度,mOriginViewY 列表 item 相對螢幕的 y 座標,與之對應的 mOriginViewXx 座標。

上邊界越界,回退的過渡動畫的起始 xy 座標與結束的 xy 保持不變,即mOriginViewXmOriginViewY 不變,只有高度發生了變化,mOriginViewVisibleHeight 可見高度變為了 mOriginViewRealHeight 真實高度:

//上邊界越界
mOriginViewVisibleHeight = mOriginViewRealHeight;

下邊界越界,需要考慮是否最後兩條 item 下越界,值為 false ,螢幕的相對 y 座標變為 statusHeight + (int) mTopNavHeightmTopNavHeight 頂部導航欄的高度。反之螢幕的相對 y 座標變為 getHeight() - (int) mBottomNavHeight - mOriginViewRealHeightmBottomNavHeight 底部導航欄的高度。同時mOriginViewVisibleHeight 可見高度變為 mOriginViewRealHeight 真實高度。

//是否最後一行資料
if (mIsLastRow) {
    mOriginViewY = getHeight() - (int) mBottomNavHeight - mOriginViewRealHeight;
} else {
    mOriginViewY = statusHeight + (int) mTopNavHeight;
}
mOriginViewVisibleHeight = mOriginViewRealHeight;

回退的過渡動畫相關程式碼如下:

final float startTransitionX = getTranslationX();
final float startTransitionY = getTranslationY();
final float startScaleX = getScaleX();
final float startScaleY = getScaleY();
final float endScaleX = (float) mOriginViewVisibleWidth / getWidth();
final float endScaleY = (float) mOriginViewRealHeight / getHeight();
...//省略相關屬性動畫初始化,以下是動畫回撥相關程式碼
float value = (float) animation.getAnimatedValue();
setScaleX(startScaleX + value * (endScaleX - startScaleX));
setScaleY(startScaleY + value * (endScaleY - startScaleY));
setTranslationX(startTransitionX + value * (mOriginViewX - startTransitionX) - value * (getWidth() - mOriginViewVisibleWidth) / 2.0F);
setTranslationY(startTransitionY - value * (startTransitionY - mOriginViewY) - value * (getHeight() - mOriginViewRealHeight) - (topOutOfBound ? value * (mOriginViewRealHeight - mOriginViewVisibleHeight) : 0));

最後提二點,第一 item 是基於 RecyclerView 實現,重寫 addItemDecoration 繪製分割線:

 mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
     @Override
     public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
         super.getItemOffsets(outRect, view, parent, state);
         int pos = parent.getChildAdapterPosition(view);
        //相關分割線繪製
     }
 });

第二 RecyclerView 移動指定的位置 position 到列表頂部,RecyclerView 自帶的幾個 API 效果並不理想,最後通過以下方式實現:

private void moveToPosition(LinearLayoutManager layoutManager, int selectedPosition) {
    int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
    int top = mRecyclerView.getChildAt(selectedPosition - firstVisiblePosition).getTop();
    mRecyclerView.scrollBy(0, top);
}

三部分的動畫組成了頭條小視訊拖拽效果,整篇實現並沒有什麼難點,細節處理好就 OVER

完結

最近一段時間我發現自己越發浮躁,懶散,學習又落下了一大截,處於這個年齡段的煩惱,生活中的無奈 … 始終堅信今後會越來越好 …

求求大家別再學習了,等等我 …

原始碼地址

為自己點個贊。

相關推薦

仿頭條視訊動畫

本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出 序言 時光荏苒,又是一年高考季,不知不覺中距離高考已經七年過去了,在我眼中依稀能夠嗅到那個夏天的味道,朋友的道別,得知成績後的苦澀,獨自靜聽大海的翻湧。。。 歲月安好,在浮躁的年紀裡,我撬

android高仿今日頭條視訊轉場切換效果

可以先看看今日頭條效果 功能分析 點選列表上的一個item,該item會放大,最後直接全屏播放小視訊,剛開始看上去,以為是個共享元素的轉場動畫, 後來想到,共享元素要在android 5.0以上支援,而今日頭條顯然不會只支援5.0版本以上 筆者想到的一種方案就是進入Acti

程式元件

<movable-area style="height: {{H*2}}rpx;width: {{W*2}}rpx; position: fixed; top: 0;"> <movable-view direction="all" style="height: 100

ajax視訊上傳(完善版)

在前輩的基礎上,進行了一些完善 功能:可實現拖拽上傳視訊,有進度條顯示,MP4格式的視訊還可實現線上播放, 進行了視訊上傳格式限制,格式不符合有提示不能上傳,大小可按情況自行新增限制 最重要的是實現了大檔案斷點續傳的功能,更詳細功能可下載後體驗,就不一 一贅述了 首先,建

一款優雅的程式排序元件實現

前言 最近po主寫小程式過程中遇到一個拖拽排序需求. 上網一頓搜尋未果, 遂自行實現. 這次就不上效果圖了, 直接掃碼感受吧. 靈感 首先由於並沒有啥現成的小程式案例給我參考. 所以有點無從下手, 那就找個h5的拖拽實現參考參考. 於是在jquery外掛網看了幾個拖拽排序實現後基本確定了思路. 大概就是用

有了這個開源 Java 專案,開發出遊戲好像不難?

本文適合有 Java 基礎知識的人群,跟著本文可學習和執行 Java 的遊戲。 本文作者:HelloGitHub-秦人 HelloGitHub 推出的《講解開源專案》系列,今天給大家帶來一款開源 Java 遊戲框架專案—— FXGLGames 專案原始碼地址:https://github.com/A

簡單又的two.js 二維動畫教程

utf-8 margin 有意 優點 adobe tom 宋體 原生 cnblogs 前 言 S N  今天呢給大家介紹一個小js框架,Two.JS。其實在自己學習的過程中並沒有找到合適的教程,所以我這種學習延遲的同學是有一定難度的,然後準備給大家整理一份,

Android 仿今日頭條頻道管理(上)(GridView之間Item的移動和

前言 經常逛今日頭條、發現它的頻道管理功能做的特別贊,互動體驗非常好、如圖: 它是2個gridview組成、2個gridview之間的Item是可以相互更換的、而且我的頻道的Item是可以拖拽進行排序。仔細觀察、今日頭條有些細節做的的非常好,當一個gridview1的item移

程式開發如何實現視訊或音訊自定義可進度條

  程式原生元件的音訊播放時並沒有進度條的顯示,而此次在我們所接的專案中,鑑於原生的視訊進度條樣式太醜,產品要求做一個可拖拽的進度條滿足需求。視訊和音訊提供的api大致是相似的,可以根據以下程式碼修改為與音訊相關的進度條。 wxml的結構如下: <video id=

仿富途牛牛-元件化(一)-支援頁籤、增刪、工具

目錄 一、概述 二、效果展示 三、實現方案分析 1、第一階段 2、第二階段 3、第三階段 一、概述 好久沒有做業務相關的UI功能

JavaScript實現預覽,AJAX文件上傳

strong 名稱 獲取文件 是否 set 可能 lis idt scrip 本地上傳,提前預覽(圖片。視頻) 1.html中div標簽預覽顯示。button標簽觸發上傳事件。 <div id="drop_area" style=

JS實現案例

res pan urb posit htm eight || move use <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> &

iOS 未讀消息角標 仿QQ 簡單靈活 支持xib(源碼)

float 簡單用法 賦值 sta navi 適應 bool isa class 一、效果 二、簡單用法 超級簡單,2行代碼集成;xib可0代碼集成,只需拖一個view關聯LFBadge類即可 支持pod導入pod ‘LFKit/LFBadge‘ //一般view上加角

android仿微信紅包動畫、Kotlin綜合應用、Xposed模塊、下拉視覺、UC瀏覽器滑動動畫等源碼

架構分析 body oot googl short html 博文 urn 管理 Android精選源碼 仿微信打開紅包旋轉動畫 使用Kotlin編寫的Android應用,內容你想象不到 Android手機上的免Root Android系統日誌Viewer 一個能讓微

js仿QQ刪除

gid 移動端 pla uic innerhtml prevent [] idt ati 原生js實現仿QQ拖拽刪除交互,無需任何依賴。 項目演示請看這裏, gitHub請移步這裏。 由於源碼很長,所以貼到最下面了。 效果截圖如下: 核心思想呢,就是點擊圓點的

js仿百度地圖、縮放、添加圖層功能(原創)

ets tle clas 火狐 相對 inner tlist posit css 最近項目中完成的需求,仿百度地圖中的功能: 要求:1.底層圖可以拖拽、縮放。    2.拖拽一個圖標,在底層圖上對應位置添加一個標註點,該標註點位置要隨底層圖移動。    3.添加的標註

程序開發如何實現視頻或音頻自定義可進度條

text 完成 我們 控制 轉載 產品 結構 可拖拽 step 程序原生組件的音頻播放時並沒有進度條的顯示,而此次在我們所接的項目中,鑒於原生的視頻進度條樣式太醜,產品要求做一個可拖拽的進度條滿足需求。視頻和音頻提供的api大致是相似的,可以根據以下代碼修改為與音頻相關的進

仿QQ訊息導航欄RadioGroup裡新增的TextView(未讀訊息)

效果圖: MainActivity: 主要是放置拖拽的TextView的位置setTextView(); 設定未讀訊息 textView.setText(“10”); public class MainActivity extends AppCompatActivity

構建狂屌的 MySQL 監控平臺

prometheus+grafana 對於現在這個時間點來說,相信很多同行都應該已經開始玩起來了,當仍然可能有一部分人可能還不知道prometheus+grafana 的存在,也可能還有一部分人雖然知道它的存在,但卻懶於動手,如果是這樣,那後面的內容你可得打起精神來了,因為可能你會被grafana炫

今日頭條、抖音、西瓜、火山、微視、陌陌等自媒體平臺視訊批量下載工具(視訊搬運福利)

前言 目前各大自媒體平臺爆火,網路流量暴漲,各大自媒體平臺的小視訊為廣大個廣告主帶來了如泉湧般的的視訊流量,更給廣大的自媒體小編帶來了豐厚的利益回報,想要創做更多的自媒體內容著實不易,下面給廣大的小視訊自媒體小編帶來一款視訊搬運的利器,《全網自媒體小視訊批量下載工具》,眾多的自媒體平臺,教你如何快速下載抖音