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

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

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

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

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();
复制代码

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


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

查看所有标签

猜你喜欢:

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

jQuery实战(第2版)

jQuery实战(第2版)

[美]Bear Bibeault、[美]Yehuda Katz / 三生石上 / 人民邮电出版社 / 2012-3 / 69.00元

jQuery 是目前最受欢迎的JavaScript/Ajax 库之一,能用最少的代码实现最多的功能。本书全面介绍jQuery 知识,展示如何遍历HTML 文档、处理事件、执行动画、给网页添加Ajax 以及jQuery UI 。书中紧紧地围绕“用实际的示例来解释每一个新概念”这一宗旨,生动描述了jQuery 如何与其他工具和框架交互以及如何生成jQuery 插件。 本书适合各层次Web 开发人......一起来看看 《jQuery实战(第2版)》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

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

HEX CMYK 互转工具