精华内容
下载资源
问答
  • 简介:android 端基于 FFmpeg 实现音频剪切、拼接、转码、混音、编解码;视频剪切、水印、截图、转码、编解码、转 Gif 动图;音视频合成与分离;音视频解码、同步与播放;FFmpeg 本地推流、H264 与 RTMP 实时推流...

    FFmpegAndroid

    项目地址:xufuji456/FFmpegAndroid 

    简介:android 端基于 FFmpeg 实现音频剪切、拼接、转码、混音、编解码;视频剪切、水印、截图、转码、编解码、转 Gif 动图;音视频合成与分离;音视频解码、同步与播放;FFmpeg 本地推流、H264 与 RTMP 实时推流直播;OpenGL+GPUImage 实时滤镜;FFmpeg 滤镜:素描、色彩平衡、hue、lut、模糊、九宫格等

    更多:作者   提 Bug   

    标签:

     

    android 端基于 FFmpeg 库的使用。
    基于 ffmpeg3.2.4 版本,编译生成 libffmpeg.so 文件。
    添加编译 ffmpeg 源码的参考脚本
    目前音视频相关处理:

    • 音频剪切、拼接

    • 音频混音

    • 音频转码

    • 音视频合成

    • 音频抽取

    • 音频解码播放

    • 音频编码

    • 视频抽取

    • 视频剪切

    • 视频转码

    • 视频截图

    • 视频降噪

    • 视频抽帧

    • 视频转 GIF 动图

    • 视频添加水印

    • 视频画面拼接

    • 视频反序倒播

    • 视频画中画

    • 图片合成视频

    • 视频解码播放

    • 本地直播推流

    • 实时直播推流

    • 音视频解码播放

    • OpenGL+GPUImage 滤镜

    • FFmpeg 的 AVFilter 滤镜

    左边是 ffplay 客户端拉流播放,中间是 web 网页播放:

    动态图片

    视频添加文字水印(文字白色背景可以改为透明):

    静态图片

    视频转成 GIF 动图:

    动态图片

    滤镜效果:

    静态图片

    静态图片

    静态图片

    静态图片

    视频画中画:

    静态图片

    视频画面拼接:

    动态图片

    视频倒播:

    动态图片

    展开全文
  • 可如果是多张图片,我们还可以将其拼接起来组成gif动图,可一张图怎么玩?记得之前写过一个小练习,把一张图片拆分成九宫格的分片图。那么,能否由此下手整出点花样呢?先来看看最终实现的两种方案吧:轮播闪现分块...

    有趣的图片

    如何能让图片变得好玩?首先需要让它动起来!可如果是多张图片,我们还可以将其拼接起来组成gif动图,可一张图怎么玩?记得之前写过一个小练习,把一张图片拆分成九宫格的分片图。那么,能否由此下手整出点花样呢?先来看看最终实现的两种方案吧:

    轮播闪现

    9411ae67864fc05abe80cfe674a0babe.gif

    分块加载

    a65fab1bf56ded855da7383e9df8bfd7.gif

    实现分析

    命令行交互

    首先,看过上面的两张动图,细心的朋友会发现,动图将原有的图片拆分为了25块,然后进行特定的拼接。那么只能拆分成25么,模式又该怎么选择呢?此时我们需要引入一个模块argparse,它是专门用作命令行参数配置的库。之前专门写过一篇针对该模块的总结文章,大家可以去看看:

    对于python命令行,你应该这么做才专业

    好了,回到当下内容,我们需要针对三项进行配置,图片路径、gif展示方式、拆分图片数量

    # -*- coding: utf-8 -*-
    # @Author   : 王翔
    # @WeChat   : King_Uranus
    # @公众号    : 清风Python
    # @Date     : 2019/9/22 22:11
    # @Software : PyCharm
    # @version  :Python 3.7.3
    # @File     : FunnyPicture.py
    
    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument("-p", "--picture", required=True,
                        help="请填写所需制作的图片全路径")
    parser.add_argument('-t', '--type', default='join',
                        choices=['join', 'alone'],
                        help="join为分块加载,alone为轮播闪现")
    parser.add_argument("-n", "--split_number", type=int, default=9,
                        choices=[9, 16, 25, 36, 49, 64, 81, 100],
                        help="选择拆分的图片数量")
    args = parser.parse_args()

    有了这些参数,我们就可以开始编写代码了…

    图片裁剪

    图片的剪裁与拆分使用什么模块呢?**from PIL import Image**简单通过Pillow的Image就可以实现相关操作了!

    看到gif图我们会发现上下存在部分的留白,这是为什么?因为不是每张图都是等宽高的,所以我们要事先准备一块白色的幕布,然后将图片居中贴在白色背景图上。幕布大小如何决定,取图片宽高的最大值,生成一张正方形的白色幕布。

    ...
    
    from PIL import Image
    img = Image.open(args.picture.replace('', '/'))
    _width, _height = img.size
    img_size = _width if _width > _height else _height
    blank_image = Image.new(self.img_mode, (self.img_size, self.img_size), color='white')
    blank_image.save(....)
    ...

    之后,就方便我们进行拆分了。

    朋友圈不能发动图

    我们的gif做好了,可以朋友圈不能发动图,这该如何是好?其实只需3行代码就能把一个gif的图片转化为视频文件。
    模块安装:pip install moviepy

    # -*- coding: utf-8 -*-
    # @Author   : 王翔
    # @WeChat   : King_Uranus
    # @公众号    : 清风Python
    # @Date     : 2019/9/22 22:11
    # @Software : PyCharm
    # @version  :Python 3.7.3
    # @File     : FunnyPicture.py
    
    import moviepy.editor as mp
    clip = mp.VideoFileClip(filename)
    clip.write_videofile('result.mp4') 

    eb540c799b1317b6d8e6e30efa263e4e.gif

    没错,就是这么简单…但该模块封装了很多子模块,总体下载还是比较大的。我们在代码中自动引入该功能,同时生成gif与MP4文件。

    总体代码

    总体代码如下:

    # -*- coding: utf-8 -*-
    # @Author   : 王翔
    # @WeChat   : King_Uranus
    # @公众号    : 清风Python
    # @Date     : 2019/9/22 22:11
    # @Software : PyCharm
    # @version  :Python 3.7.3
    # @File     : FunnyPicture.py
    
    import argparse
    from PIL import Image
    import os
    import copy
    import moviepy.editor as mp
    
    BasePath = os.path.dirname(os.path.realpath(__file__))
    
    
    class FunnyPicture:
        def __init__(self):
    
            self.img_mode = None
            self.img_size = None
            self.blank_image = None
            self.git_list = list()
            # 获取图片名称(去除后缀名)
            self.picture_name = os.path.splitext(os.path.split(args.picture)[1])[0]
            self.save_path = os.path.join(BasePath, self.picture_name)
            if not os.path.exists(self.save_path):
                os.mkdir(self.save_path)
            # 格式化图片路径
            self.picture = self.resize_picture()
    
        def resize_picture(self):
            img = Image.open(args.picture.replace('', '/'))
            self.img_mode = img.mode
            _width, _height = img.size
            self.img_size = _width if _width > _height else _height
            self.blank_image = Image.new(self.img_mode, (self.img_size, self.img_size), color='white')
            self.blank_image.save(os.path.join(self.save_path, '{}_blank.jpg'.format(self.picture_name)))
            _image = copy.copy(self.blank_image)
            if _width > _height:
                _image.paste(img, (0, int((self.img_size - _height) / 2)))
            else:
                _image.paste(img, (int((self.img_size - _width) / 2), 0))
            return _image
    
        def split_picture(self):
            size = int(args.split_number ** 0.5)
            side_len = int(self.img_size / size)
            _index = 1
            blank_image = copy.copy(self.blank_image)
            for i in range(0, size):
                for j in range(0, size):
                    if args.type != "join":
                        blank_image = copy.copy(self.blank_image)
                    per_size = (j * side_len, i * side_len, (j + 1) * side_len, (i + 1) * side_len)
                    per_img = self.picture.crop(per_size)
                    blank_image.paste(per_img, (j * side_len, i * side_len))
                    self.git_list.append(copy.copy(blank_image))
                    # 希望保留部分图片内容的可以取消注释
                    # 中途的每一块局部图
                    # per_img.save(os.path.join(self.save_path, '{}_per{}.jpg'.format(self.picture_name, _index)))
                    # 动图的每一帧图片
                    # blank_image.save(os.path.join(self.save_path, '{}_per_gif{}.jpg'.format(self.picture_name, _index)))
                    _index += 1
    
        def composite_gif(self):
            images = []
            im = Image.open(os.path.join(self.save_path, '{}_blank.jpg'.format(self.picture_name)))
            for per_gif in self.git_list:
                images.append(per_gif)
            for i in range(10):
                images.append(self.picture)
            gif_name = "{}_result.gif".format(os.path.join(self.save_path, self.picture_name))
            im.save(gif_name, save_all=True, loop=True, append_images=images, duration=200)
            self.composite_mp4(gif_name)
    
        @staticmethod
        def composite_mp4(filename):
            clip = mp.VideoFileClip(filename)
            clip.write_videofile(os.path.splitext(filename)[0] + '.mp4')
    
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser()
        parser.add_argument("-p", "--picture", required=True,
                            help="请填写所需制作的图片全路径")
        parser.add_argument('-t', '--type', default='join',
                            choices=['join', 'alone'],
                            help="join为分块加载,alone为轮播闪现")
        parser.add_argument("-n", "--split_number", type=int, default=9,
                            choices=[9, 16, 25, 36, 49, 64, 81, 100],
                            help="选择拆分的图片数量")
        args = parser.parse_args()
        main = FunnyPicture()
        main.split_picture()
        main.composite_gif()

    关于打包

    既然为了好玩,当然要打包成exe的可执行文件喽。但关于打包,需要说明两点:

    1.通过pyinstaller打包的软件,会被杀软误报。解压、使用时需添加白名单。

    2.刚才说到moviepy依赖的模块太多,打包会导致异常,所以未将次功能进行打包,如果喜欢创建视频的,可以使用3行命令单独执行下。

    来看看打包效果:

    5aeaea30179417dcbe310f6214d8e662.gif

    更多精彩内容,请滑至顶部点击右上角关注小宅哦~

    9778183ba0d2b707f5c5b09cdcacb346.png

    作者:清风Python

    展开全文
  • FFmpegAndroid,android 端基于 FFmpeg 实现音频剪切、拼接、转码、混音、编解码;视频剪切、水印、截图、转码、编解码、转 Gif 动图;音视频合成与分离;音视频解码、同步与播放;FFmpeg 本地推流、H264 与 RTMP ...
  • 操作比较简单,我就不叙述了,直接上动图。 1、分割代码片段 2、使用表达式生成一行代码 3、批量生成代码 转载于:https://www.cnblogs.com/duduhuo/p/6107710.html...

    操作比较简单,我就不叙述了,直接上动图。

    1、分割代码片段

    Step1

    2、使用表达式生成一行代码

    Step2

    3、批量生成代码

    Step3

    转载于:https://www.cnblogs.com/duduhuo/p/6107710.html

    展开全文
  • 预备接手表情包处理业务,前期处理并不复杂,流程包括 : GIF动图与视频的解帧 , 逐帧处理, 组合各帧得到新的GIF. 经过调研, 整合了ffmpeg的Java CV 可完美处理解帧 ,animated-gif-lib 组件包含gif生成的成熟方案 , ...

      预备接手表情包处理业务,前期处理并不复杂,流程包括 : GIF动图与视频的解帧 , 逐帧处理, 组合各帧得到新的GIF. 经过调研, 整合了ffmpeg的Java CV 可完美处理解帧 , animated-gif-lib 组件包含gif生成的成熟方案 , 进而问题解决.

      animated-gif-lib + Java CV

      animated-gif-lib.jar是用来拆分和合成GIF的工具包,主要用到其中的GifDecoder/AnimatedGifEncoder.

      Java CV 常用于音频/图片等处理,其中整合了常用的c++类库,例如音频处理的ffmpeg,且可与Open CV配合使用.这里主要用到FFmpegFrameGrabber来取帧/Java2DFrameConverter来类型转换.

      其实,GifDecoder也可以完成对GIF的解帧,但无法对视频进行操作,且实际使用中发现各帧颜色处理上有偏差,但并不影响最后新GIF的合成.综上,为了代码的复用性,采用Java CV来解帧,只使用其中AnimatedGifEncoder来完成合成GIF的操作.

      代码实现

      解帧,FFmpegFrameGrabber获取GIF总帧数时异常(),故而采用GifDecoder获取

            String gifPath = "/home/lab/test/11.gif";
            String dirPath = "/home/lab/test/gif/";
            // 用以解帧
            FFmpegFrameGrabber grabberGif = new FFmpegFrameGrabber(gifPath);
            grabberGif.start();
            Frame frame ;
            // 用以获取GIF总帧数
            GifDecoder decoder = new GifDecoder();
            int status = decoder.read(gifPath);
            if (status != GifDecoder.STATUS_OK) {
                throw new IOException("read image " + gifPath + " error!");
            }
            // 类型转换,Frame -> BufferedImage
            Java2DFrameConverter converter = new Java2DFrameConverter();
            int frameCount = decoder.getFrameCount();
            for (int i = 0 ; i < frameCount ; i++) {
                String fileName = dirPath + "img_" + i + ".jpg";
                File outPut = new File(fileName);
                frame = grabberGif.grabImage();
                if (frame != null) {
                    ImageIO.write(converter.getBufferedImage(frame),"jpg",outPut);
                }
            }
            grabberGif.stop();

      合成GIF

            int frameRate = 20;// 新GIF总帧数
            String resGif = "/home/lab/test/22.gif";
            FileOutputStream targetFile = new FileOutputStream(resGif); // 目标文件流
            int margin = 2; // 间隔帧数
            AnimatedGifEncoder en = new AnimatedGifEncoder();
            en.setFrameRate(frameRate);
            en.start(targetFile);
            for (int i = 0; i < frameRate; i++) {
                en.addFrame(converter.convert(grabberGif.grabImage()));
                grabberGif.setFrameNumber(grabberGif.getFrameNumber() + margin);
            }
            en.finish();
            grabberGif.stop();
            targetFile.close();

      原GIF倒序得到新GIF

            String gifPath = "/home/lab/test/11.gif";
            // 用以解帧
            FFmpegFrameGrabber grabberGif = new FFmpegFrameGrabber(gifPath);
            grabberGif.start();
            // 用以获取GIF总帧数
            GifDecoder decoder = new GifDecoder();
            int status = decoder.read(gifPath);
            if (status != GifDecoder.STATUS_OK) {
                throw new IOException("read image " + gifPath + " error!");
            }
            // 类型转换,Frame -> BufferedImage
            Java2DFrameConverter converter = new Java2DFrameConverter();
            int frameCount = decoder.getFrameCount();
            String resGif = "/home/lab/test/22.gif";
            FileOutputStream targetFile = new FileOutputStream(resGif); // 目标文件流
            AnimatedGifEncoder en = new AnimatedGifEncoder();
            en.setFrameRate(frameCount);
            en.start(targetFile);
            for (int i = frameCount - 1; i >= 0; i--) {
                grabberGif.setFrameNumber(i);
                en.addFrame(converter.convert(grabberGif.grabImage()));
            }
            en.finish();
            grabberGif.stop();
            targetFile.close();

      基于GifDecoder和AnimatedGifEncoder实现的gif倒序

            String outputPath = "/home/lab/test/001.gif";
            String imagePath = "/home/lab/test/33.gif";
            GifDecoder decoder = new GifDecoder();
            int status = decoder.read(imagePath);
            if (status != GifDecoder.STATUS_OK) {
                throw new IOException("read image " + imagePath + " error!");
            }
            // 拆分一帧一帧的压缩之后合成
            AnimatedGifEncoder encoder = new AnimatedGifEncoder();
            encoder.start(outputPath);
            encoder.setRepeat(decoder.getLoopCount());
            for (int i = decoder.getFrameCount() -1; i >= 0; i--) {
                encoder.setDelay(decoder.getDelay(i));// 设置播放延迟时间
                BufferedImage bufferedImage = decoder.getFrame(i);// 获取每帧BufferedImage流
                int height = bufferedImage.getHeight();
                int width = bufferedImage.getWidth();
                BufferedImage zoomImage = new BufferedImage(width, height, bufferedImage.getType());
                Image image = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
                Graphics gc = zoomImage.getGraphics();
                gc.setColor(Color.WHITE);
                gc.drawImage(image, 0, 0, null);
                encoder.addFrame(zoomImage);
            }
            encoder.finish();
            File outFile = new File(outputPath);
            BufferedImage image = ImageIO.read(outFile);
            ImageIO.write(image, outFile.getName(), outFile);

       视频转gif

            String videpPath = "/home/lab/test/t1.mp4";
            String gifPath = "/home/lab/test/test.gif";
            FileOutputStream targetFile = new FileOutputStream(gifPath);
            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videpPath);
            grabber.start();
            Frame frame;
            int frames = grabber.getLengthInFrames();
            AnimatedGifEncoder encoder = new AnimatedGifEncoder();
            encoder.setFrameRate(frames);
            encoder.start(targetFile);
            Java2DFrameConverter converter = new Java2DFrameConverter();
            for (int i = 0 ; i < frames ; i++) {
                encoder.setDelay((int) grabber.getDelayedTime());
                grabber.setFrameNumber(i);
                frame = grabber.grabImage();
                encoder.addFrame(converter.convert(frame));
            }
            encoder.finish();
            targetFile.close();
            grabber.close();

      pom依赖

      因 javacv-platform依赖过重,实际引入的时候推荐指定系统版本的即可.开发机为64位Ubuntu,依赖如下

            <dependency>
                <groupId>org.bytedeco</groupId>
                <artifactId>javacv</artifactId>
                <version>1.4.3</version>
            </dependency>
            <dependency>
                <groupId>org.bytedeco.javacpp-presets</groupId>
                <artifactId>opencv</artifactId>
                <version>3.4.3-1.4.3</version>
                <classifier>linux-x86_64</classifier>
            </dependency>
            <dependency>
                <groupId>org.bytedeco.javacpp-presets</groupId>
                <artifactId>ffmpeg</artifactId>
                <version>4.0.2-1.4.3</version>
                <classifier>linux-x86_64</classifier>
            </dependency>
            <dependency>
                <groupId>org.bytedeco</groupId>
                <artifactId>javacpp</artifactId>
                <version>1.4.3</version>
            </dependency>
            <dependency>
                <groupId>org.bytedeco.javacpp-presets</groupId>
                <artifactId>ffmpeg</artifactId>
                <version>4.0.2-1.4.3</version>
                <classifier>linux-x86_64</classifier>
            </dependency>
            <!-- gif -->
            <dependency>
                <groupId>com.madgag</groupId>
                <artifactId>animated-gif-lib</artifactId>
                <version>1.4</version>
            </dependency>

     

    转载于:https://www.cnblogs.com/nyatom/p/10837363.html

    展开全文
  • 去缝的教程老沙写过不少:关于静态图与动图的《长图无缝拼接动图照样搞定!》关于音频与图片的《图片与音频的缝隙,去得干干净净!》关于视频与图片的《视频尺寸改版出现白条?最新图片、视频无缝连接教程来了!》...
  • stegsolve.rar

    2020-05-01 21:16:26
    stegsolve 图片隐写必备 可以查看图片的具体信息、抽取图片中的隐藏数据、对GIF之类的动图进行分解,动图变成一张张图片,便于查看、图片拼接
  • 可如果是多张图片,我们还可以将其拼接起来组成gif动图,可一张图怎么玩?记得之前写过一个小练习,把一张图片拆分成九宫格的分片图。那么,能否由此下手整出点花样呢?先来看看最终实现的两种方案吧: 轮播闪现 ...
  • 金三胖 题目中给出了一张gif格式动图,在动图中可以看出夹杂了题目的flag,使用jar对图片进行处理,在逐帧图片中我们可以得到题目的flag 将图片中的flag进行拼接即可得到答案
  • Stegsolve.zip

    2019-09-05 18:10:04
    隐写图片查看的神器 File Format:文件格式,这个主要是查看图片的具体信息 ...Frame Browser:帧浏览器,主要是对GIF之类的动图进行分解,动图变成一张张图片,便于查看 Image Combiner:拼图,图片拼接
  • js 实现纯前端将数据导出excel两种方式,亲测有效

    万次阅读 多人点赞 2018-05-23 14:33:27
    由于项目需要,需要在不调用后台接口的情况下,将json数据导出到excel表格,参考了好多资料以及很多大佬写的博客终于实现,兼容chrome没问题,其他还没有测试过,这边介绍两种实现方式,并附上代码和gif动图,博主...
  • 将文件下载之后解压,可以用...Frame Browser: 帧浏览器,主要是对GIF之类的动图进行分解,动图变成一张张图片,便于查看 Image Combiner: 拼图,图片拼接 所以在用stegsolve.jar将文件打开之后点击analyse再点
  • python图片操作

    2020-03-17 22:08:21
    1:图片拼接动图 import imageio outfilename=“laoban.gif” filenames=[] for i in range(13): filename=str(i+1) filenames.append(filename+’.jpg’) frames=[] for image in filenames: im=imageio.imread...
  • 视频转GIF动图 视频添加水印 视频画面拼接 视频反序倒播 视频画中画 图片合成视频 视频解码播放 本地直播推流 实时直播推流 音视频解码播放 OpenGL+GPUImage滤镜 FFmpeg的AVFilter滤镜 使用mp3lame库进行mp3转码 ...
  • 本题难点在于括号内嵌套括号,需要从内向外生成与拼接字符串,这与栈的先入后出特性对应。 动图演示: class Solution { public String decodeString(String s) { StringBuilder res = new StringBuilder(); ...
  • 这几天一直在开发h5仿钉钉项目,使用到了html5+css3+zepto+swiper+wcPop等技术进行开发,实现了消息、表情、动图gif发送,仿QQ、微信多人图像拼接,可以选择本地图片,并可以图片、视频预览,仿微信...
  • android端采用FFmpeg进行视频剪切、转码与添加水印

    万次阅读 多人点赞 2018-01-25 02:16:16
    前两篇文章介绍过FFmpeg进行音频处理、音视频处理:android端采用FFmpeg进行音频混合与拼接剪切, android端采用FFmpeg进行音视频合成与分离。关于FFmpeg涉及文件导入以及cmake配置,可查看第一篇文章。现在接着...
  • 在截图编辑和 GIF 制作方面,iOS 平台有诸多独占且优秀的应用可以使用,例如利用 Picsew 截图拼接、Annotable 截图标注、OneScreen 带壳截图以及 ImgPlay Pro 制作 GIF 动图。而当我将主力机切换到 Android 后(非...
  • 转载自:TCP的三次握手与四次挥手(详解+动图) TCP的概述 TCP把连接作为最基本的对象,每一条TCP连接都有两个端点,这种断点我们叫作套接字(socket),它的定义为端口号拼接到IP地址即构成了套接字,例如...
  • 将table标签,包括tr、td等对json数据进行拼接,将table输出到表格上实现,这种方法的弊端在于输出的是伪excel,虽说生成xls为后缀的文件,但文件形式上还是html,代码如下 <html> <head> <p style=...
  • 下面对梯度下降过程进行动图演示,可以修改不同的学习率,观看效果。 import numpy as np import matplotlib.pyplot as plt from IPython import display X = 2*np.random.rand(100,1) y = 4+3*X+np.random.randn...
  • 由于项目需要,需要在不调用后台接口的情况下,将json数据导出到excel表格,参考了好多资料以及很多大佬写的博客终于实现,兼容chrome没问题,其他还没有测试过,这边介绍两种实现方式,并附上代码和gif动图,博主...
  • 支持多种分辨率选择、实时美颜、实时滤镜、摄像头切换、闪光灯切换、对接人脸识别SDK实现人脸贴图等多样的录制功能、支持视频画面和时长裁剪、多视频拼接、添加滤镜、动图、音乐、MV、字幕、涂鸦等短视频高级编辑。...
  • 支持多种分辨率选择、实时美颜、实时滤镜、摄像头切换、闪光灯切换、对接人脸识别SDK实现人脸贴图等多样的录制功能、支持视频画面和时长裁剪、多视频拼接、添加滤镜、动图、音乐、MV、字幕、涂鸦等短视频高级编辑。...
  • 由于项目需要,需要在不调用后台接口的情况下,将json数据导出到excel表格,参考了好多资料以及很多大佬写的博客终于实现,兼容chrome没问题,其他还没有测试过,这边介绍两种实现方式,并附上代码和gif动图,博主...
  • 由于项目需要,需要在不调用后台接口的情况下,将json数据导出到excel表格,参考了好多资料以及很多大佬写的博客终于实现,兼容chrome没问题,其他还没有测试过,这边介绍两种实现方式,并附上代码和gif动图,博主...

空空如也

空空如也

1 2 3
收藏数 43
精华内容 17
关键字:

动图拼接