MediaStream 实现带摄像头捕捉的表情包制作器

栏目: Html5 · 发布时间: 6年前

内容简介:之前恶搞了一张朋友的表情包,直接在百度上找了一个在线表情包制作器,突然灵光一闪,要是支持摄像头该多好,方便又快捷 (重点是省手机内存,不用拍照 :) ),二话不说,开始搬砖使用的第三方库

之前恶搞了一张朋友的表情包,直接在百度上找了一个在线表情包制作器,突然灵光一闪,要是支持摄像头该多好,方便又快捷 (重点是省手机内存,不用拍照 :) ),二话不说,开始搬砖

MediaStream 实现带摄像头捕捉的表情包制作器

体验地址

2. 预想的功能点

图片支持直接粘贴 和 拖拽
图片和文字缩放,支持鼠标滚轮
支持图片翻转
支持捕捉摄像头画面当素材

3. 撸页面

使用的第三方库

antd
react-color
react-draggle
dom-to-image

页面 使用 React + Antd 方便快捷,三下五除二就搞定了

//每一行基本就是这样子
    const operationRow = ({ icon = "edit", label, component }) => (
      <Row className={`${prefix}-item`}>
        <Col span={labelSpan} className={`${prefix}-item-label`}>
          <Button type="dashed" icon={icon}>
            {label}
          </Button>
        </Col>
        <Col
          span={valueSpan}
          offset={offsetSpan}
          className={`${prefix}-item-input`}
        >
          {component}
        </Col>
      </Row>
    );
复制代码

支持图片拖拽

dragArea.addEventListener(
      "dragleave",
      e => {
        this.stopAll(e);
        this.removeDragAreaStyle();
      },
      false
    );
    //移动
    dragArea.addEventListener(
      "dragover",
      e => {
        this.stopAll(e);
        this.addDragAreaStyle();
      },
      false
    );
    dragArea.addEventListener(
      "drop",
      e => {
        this.stopAll(e);
        this.removeDragAreaStyle();
        const files = e.dataTransfer.files;
        this.renderImage(Array.from(files)[0]);
      },
      false
    );
复制代码

支持 图片 粘贴,这个也很简单 绑定粘贴事件 拿到 event 里面的 data 渲染出来就行了

pasteHandler = e => {
    const { items, types } = e.clipboardData;
    if (!items) return;

    const item = items[0]; //只要一张图片
    const { kind, type } = item; //kind 种类 ,type 类型
    if (kind.toLocaleLowerCase() != "file") {
      return message.error("错误的文件类型!");
    }
    const file = item.getAsFile();
    this.renderImage(file);
  };
  //粘贴图片
  bindPasteListener = area => {
    area.addEventListener("paste", this.pasteHandler);
  };
复制代码

渲染图片

renderImage = file => {
    if (file && Object.is(typeof file, "object")) {
      let { type, name, size } = file;
      if (!isImage(type)) {
        return message.error("无效的图片格式");
      }
      this.setState({ loading: true });
      const url = window.URL.createObjectURL(file);
      this.setState({
        currentImg: {
          src: url,
          size: `${~~(size / 1024)}KB`,
          type
        },
        scale: defaultScale,
        loading: false,
        loadingImgReady: true
      });
    }
  };
复制代码

其他就没啥好说的了,常规的页面布局

4. 生成图片

生成图片 本质上就是 利用 canvas 的 ctx.drawImage() ,

绘制文字

const canvas = document.createElement('cavans')
const ctx = canvas.getContext('2d')

canvas.width = "预览区域的宽"
canvas.height = "预览区域的高"
//图片
ctx.drawImage("URL",...attr)
//文字
ctx.fillText(TEXT,...attr)
//旋转图片
ctx.rotate(Math.PI/180 * 好多度)
//缩放 也是 调用 drawImage, 改变 sy,sx 绘制的其实就实现缩放了
ctx.drawImage(URL,0,0,sx /scale,sy/scale,0,0,x,y)

//转成图片
canvas.toDataURL('image/png',如果要压缩这里就填第二个参数)
复制代码

懂原理了 其实没必要一行一行这样写了 找到个 现成的 dom-to-image 的库,肥肠的不错,也是开源和组件化得魅力啊,利人利己

调用 api domToimage.toPng() 轻松搞定

drawMeme = () => {
    const { width, height, loadingImgReady,isCompress } = this.state;
    if (!loadingImgReady) return message.error("请选择图片!");

    this.setState({ drawLoading: true });

    const imageArea = document.querySelector(".preview-content");
    const options = {
      width,
      height,
    }
    if(isCompress){
      options.quality = defaultQuality
    }
    domToImage
      .toPng(imageArea, options)
      .then(dataUrl => {
        this.setState({ drawLoading: false });
        Modal.confirm({
          title: "生成成功",
          content: <img src={dataUrl} style={{ maxWidth: "100%" }} />,
          onOk: () => {
            message.success("下载成功!");
            const filename = Date.now()
            const ext = isCompress ? 'jpeg' : 'png'
            var link = document.createElement("a");
            link.download = `${filename}.${ext}`;
            link.href = dataUrl;
            link.click();
          },
          okText: "立即下载",
          cancelText: "再改一改"
        });
      })
      .catch(err => {
        message.error(err);
        this.setState({ drawLoading: false });
      });
  };
复制代码
  1. MediaStream 实现 摄像头捕捉

要想拿到 MediaStream , 调用 navigatar.mediaDevices() 即可, 如果想研究 webRTC ,这些 API 也是基础, 个人不是很感兴趣,就没研究,暂时只用到这个借口

navigator.mediaDevices
        .getUserMedia({
          video: true,
          audio: true
        })
        .then(stream => {
          const cameraUrl = window.URL.createObjectURL(stream);
          const hide = message.loading('盛世美颜即将出现...')
          //其他代码
          this.setState(
            {
              cameraUrl,
              cameraVisible: true
            },
            () => {
              setTimeout(()=>{
                try {
                  this.video.play();
                } catch (err) {
                  console.log(err);
                  Modal.error({
                    title: "摄像头失败",
                    content: err.message
                  });
                } finally{
                  hide()
                }
              },1000)
              
            }
          );
        })
        .catch((err)=>{
        
          console.log(err)
          Modal.error({
            title: "调用摄像头失败",
            content: err.toString()
          });
          this.setState({ cameraVisible: false });
        });
        })
复制代码

这时页面左上角 会弹出一个提示 问你是不是允许 使用摄像头,同意后 拿到 stream ,否则进入 cath

MediaStream 实现带摄像头捕捉的表情包制作器

使用 URL.createObjectURL() 拿到一个临时的 url 链接

然后将 链接 设置成 <video src={临时 URL}/> 调用 video.play()

//jsx
          <video
            style={{
              display:"block",
              margin:"0 auto"
            }}
            ref={video => (this.video = video)}
            src={cameraUrl}
            width={previewContentStyle.width}
            height={previewContentStyle.height}
          />
复制代码

这时就会看见一个 帅气的脸庞 出现在了屏幕上 !

这时来到了最后一步,截取画面,也很简单 把 video 节点画在 canvas 上, 然后 toDataURL() 蹬蹬,搞定

screenShotCamera = () => {
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    const { width, height } = previewContentStyle;
    canvas.width = width;
    canvas.height = height;
    ctx.drawImage(this.video, 0, 0, width, height);
    const data = canvas.toDataURL("image/png");
    message.success('截取摄像头画面成功!')
    this.setState({
      currentImg: {
        src: data
      },
      cameraVisible:false,
      scale: defaultScale,
      loading: false,
      loadingImgReady: true
    });
  };
复制代码

5. 下载图片

下载图片 基于 HTML5download 属性很好实现

const filename = Date.now()
    const ext = isCompress ? 'jpeg' : 'png'
    const link = document.createElement("a");
    link.download = `${filename}.${ext}`;
    link.href = dataUrl;
    link.click();
复制代码

然后默认触发一次点击事件 搞定


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据结构与问题求解

数据结构与问题求解

韦斯 / 清华大学出版社 / 2011-8 / 89.50元

《数据结构与问题求解(Java语言版)(第4版)》是专为计算机科学专业的两个学期课程而设计的,从介绍什么足数据结构开始,继而对高级数据结构与算法进行分析。《数据结构与问题求解(Java语言版)(第4版)》以独特的方式,清晰地将每种数据结构的接口与其实现分离开来,即将如何使用数据结构与如何对数据结构编程相分离。《数据结构与问题求解(Java语言版)(第4版)》从抽象思维和问题求解的角度出发,为数据结......一起来看看 《数据结构与问题求解》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具