名是个代号

名是个代号 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

名是个代号 提出了问题 · 10月16日

有关图片格式jpg转换成png格式

由于商品列表的商品图片都是jpg白底的,而界面风格是深色的,导致很有违和感。然后就想到前端能不能独立完成将图片格式jpg转换成png呢?
抱着该疑问,使用了Canvas将白色背景的jpg转成透明背景的png,参考网址为https://segmentfault.com/a/1190000003718702
一两张还好,多张就不太行了,有没有好的解决方案或者其它方法呢

关注 2 回答 2

名是个代号 回答了问题 · 9月27日

element菜单刷新后定位问题?

楼主问题解决了吗

关注 3 回答 2

名是个代号 收藏了文章 · 6月30日

【Copy攻城狮日志】踩坑小程序之canvas的显示层级问题

Created 2019-4-3 18:29:53 by huqi
Updated 2019-4-3 19:12:22 by huqi

clipboard.png

↑开局一张图,故事全靠编↑

从一个需求说起

狼叔@i5ting 曾说过:“单纯讲技术进阶点意义不大,脱离场景都是耍流氓”。今天,依旧从一个需求说起。什么需求呢?一个二维码,一个二次确认弹窗。这里的二维码是前端生成的,二维码下边有个button,点击button调起自定义的弹窗组件。依旧是很简单的需求,但是对于“资深”的Copy攻城狮来说,除了布局,其他的就只能去Copy了。分析了一下可能需要的代码,就开始'刷刷刷'一顿CP(Copy&Paste)操作猛如虎,结果跑下代码发现error二百五。特别是真机跑的时候,问题特别多。像这次的问题,开发者工具上压根就发现不了,幸好习惯性真机预览,不然一通push就等着失业了。还是坑在基础不牢固,文档看得不深入,对小程序原生组件应该注意的事项把握不准,才会掉入这个非常基础的坑。

clipboard.png
(图片来源于网络)

canvas生成二维码

通常来说,遇到这种类似的需要,我都会先找找被人造的轮子,尝试一下,有合适的就直接拿过来用了。这次用的是@yingye 大佬开源的weapp-qrcode,这个js应该是借鉴了jquery-qrcodenode-qrcode,有兴趣的同学可以研究研究,生码的逻辑应该是类似的,只是小程序中没有DOM操作,都是利用canvas来实现的。具体怎么实现,各位看客可以直接看相关的源码或文档。我的实现:

wxml

<canvas style="width: 140px; height: 140px;" canvas-id="myQrcode"></canvas>

wxss

canvas{
  display: block;
  margin: 0rpx auto;  /** 居中 **/
}

js

    drawQrcode({
      width: 140,  // 必须,二维码宽度,与canvas的width保持一致
      height: 140, // 必须,二维码高度,与canvas的height保持一致
      x: 0, // 非必须,二维码绘制的 x 轴起始位置,默认值0
      y: 0, // 非必须,二维码绘制的 y 轴起始位置,默认值0
      canvasId: 'myQrcode', // 非必须,绘制的canvasId
      typeNumber: 10, // 非必须,二维码的计算模式,默认值-1
      text: '您的二维码内容',  // 必须,二维码内容
      callback(e) { // 非必须,绘制完成后的回调函数
        console.log('e: ', e)
      }
    })

二维码效果:

clipboard.png

canvas使用限制

当我页面如上图一样。底部有个按钮。点击唤起自定义的弹窗组件,在开发者工具上呈现的效果十分正常。但是在真机上就会出现文字开头的不和谐现象。canvas直接覆盖住了自定义组件。通过翻阅文档,您会发现官方特别写出了Bug&Tip

然后点开原生组件使用限制,就会发现本B.U.G的根本原因了:

  • 原生组件的层级是最高的,所以页面中的其他组件无论设置 z-index 为多少,都无法盖在原生组件上。

也就是说canvas会覆盖自定义的dialog组件。那么怎么解决呢?我的思路是“曲线救国”--将canvas转成image。一不做二不休,撸起袖子,开干!

clipboard.png

将canvas转换成image

既然原生组件(cameracanvasfocus时的inputlive-playerlive-pushermaptextareavideo)这么牛逼,那就打压一下,去掉他们高贵的身份,豁免他们享有的特权,彻底ge他们的命,恢复他们的平民身份。按照这个思路,开始一步一步来实现
wxml

 <canvas wx:if="{{!renderImg}}"  style="width: 140px; height: 140px;" canvas-id="myQrcode"></canvas>
 <image wx:else mode="scaleToFill" class="image" style="width: 140px; height: 140px;" data-original="{{renderImg}}"></image>

js

    data: {
      renderImg: ''
    },
    onLoad: function(){
        drawQrcode({
          width: 140,  // 必须,二维码宽度,与canvas的width保持一致
          height: 140, // 必须,二维码高度,与canvas的height保持一致
          x: 0, // 非必须,二维码绘制的 x 轴起始位置,默认值0
          y: 0, // 非必须,二维码绘制的 y 轴起始位置,默认值0
          canvasId: 'myQrcode', // 非必须,绘制的canvasId
          typeNumber: 10, // 非必须,二维码的计算模式,默认值-1
          text: '您的二维码内容',  // 必须,二维码内容
          callback(e) { // 非必须,绘制完成后的回调函数
            console.log('e: ', e)
            if(e.errMsg == 'drawCanvas:ok') { // 新增转图片
              wx.canvasToTempFilePath({
                x: 0,
                y: 0,
                width: 140,
                height: 140,
                canvasId: 'myQrcode',
                success: function(res) {
                  me.setData({ renderImg: res.tempFilePath});
                }
              });   
            }
          }
        })
    }

以上将canvas替换成image,不过遇到闪烁的问题,这是wx:if特有的,这里通过取巧的办法,只改了canvas的样式:
wxss

canvas{
  display: block;
  margin: 0rpx -9999px;  /** 占位解决二维码闪屏 **/
}
image{
  display: block;
  margin: 0rpx auto;  /** 居中 **/
}

至此,已填了这个canvas显示层级过高的坑。

clipboard.png

如您有更好的方案,欢迎提出指正!
如您觉得文章解决了您的问题,欢迎打赏

查看原文

名是个代号 关注了专栏 · 6月30日

胡琦的博客

胡琦的博客,记录一个末流程序员的成长轨迹!

关注 3

名是个代号 回答了问题 · 3月23日

解决videoJs toDataURL对视频截图疑难问题

已有解决方案了。

关注 7 回答 6

名是个代号 收藏了文章 · 3月18日

记录一波video.js的使用及问题

最近的项目中需要播放视频,鉴于html5元素<video>的一些坑及不想自己造轮子,于是就找到了web端播放视频使用量最多的插件video.js,video.js是国外开发者开发的,英语本身就不好的我看英文文档简直是折磨,国内又没有中文文档,能搜的到的基本是简单的使用及最基本的api的介绍,想要实现一些自定义功能无从下手,所以我在这里整理一份我所遇到的问题及解决方法

1、视频初始化

video.js有两种初始化方式,一种是在video的html标签之中,一种是使用js来进行初始化

1.1、在video中进行初始化

<video
    id="my-player"
    class="video-js"
    controls
    preload="auto"
    poster="//vjs.zencdn.net/v/oceans.png"
    width="600"
    height="400"
    data-setup='{}'>
  <source data-original="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4"></source>
  <source data-original="//vjs.zencdn.net/v/oceans.webm" type="video/webm"></source>
  <source data-original="//vjs.zencdn.net/v/oceans.ogv" type="video/ogg"></source>
  <p class="vjs-no-js">
    To view this video please enable JavaScript, and consider upgrading to a
    web browser that
    <a href="https://videojs.com/html5-video-support/" target="_blank">
      supports HTML5 video
    </a>
  </p>
</video>

效果
图片描述

1.2、使用js进行初始化

<!-- vjs-big-play-centered可使大的播放按钮居住,vjs-fluid可使视频占满容器 -->
<video id="myVideo" class="video-js vjs-big-play-centered vjs-fluid">
  <p class="vjs-no-js">
    To view this video please enable JavaScript, and consider upgrading to a
    web browser that
    <a href="https://videojs.com/html5-video-support/" target="_blank">
      supports HTML5 video
    </a>
  </p>
</video>

<script>
var player = videojs(document.getElementById('myVideo'), {
  controls: true, // 是否显示控制条
  poster: 'xxx', // 视频封面图地址
  preload: 'auto',
  autoplay: false,
  fluid: true, // 自适应宽高
  language: 'zh-CN', // 设置语言
  muted: false, // 是否静音
  inactivityTimeout: false,
  controlBar: { // 设置控制条组件
    /* 设置控制条里面组件的相关属性及显示与否
    'currentTimeDisplay':true,
    'timeDivider':true,
    'durationDisplay':true,
    'remainingTimeDisplay':false,
    volumePanel: {
      inline: false,
    }
    */
    /* 使用children的形式可以控制每一个控件的位置,以及显示与否 */
    children: [
      {name: 'playToggle'}, // 播放按钮
      {name: 'currentTimeDisplay'}, // 当前已播放时间
      {name: 'progressControl'}, // 播放进度条
      {name: 'durationDisplay'}, // 总时间
      { // 倍数播放
        name: 'playbackRateMenuButton',
        'playbackRates': [0.5, 1, 1.5, 2, 2.5]
      },
      {
        name: 'volumePanel', // 音量控制
        inline: false, // 不使用水平方式
      },
      {name: 'FullscreenToggle'} // 全屏
    ]
  },
  sources:[ // 视频源
      {
          src: '//vjs.zencdn.net/v/oceans.mp4',
          type: 'video/mp4',
          poster: '//vjs.zencdn.net/v/oceans.png'
      }
  ]
}, function (){
  console.log('视频可以播放了',this);
});
</script>

2、controlBar组件的说明

  • playToggle, //播放暂停按钮
  • volumeMenuButton,//音量控制
  • currentTimeDisplay,//当前播放时间
  • timeDivider, // '/' 分隔符
  • durationDisplay, //总时间
  • progressControl, //点播流时,播放进度条,seek控制
  • liveDisplay, //直播流时,显示LIVE
  • remainingTimeDisplay, //当前播放时间
  • playbackRateMenuButton, //播放速率,当前只有html5模式下才支持设置播放速率
  • fullscreenToggle //全屏控制
currentTimeDisplay,timeDivider,durationDisplay是相对于 remainingTimeDisplay的另一套组件,后者只显示当前播放时间,前者还显示总时间。若要显示成前者这种模式,即 '当前时间/总时间',可以在初始化播放器选项中配置:
var myPlayer = neplayer('my-video', {controlBar:{
    'currentTimeDisplay':true,
    'timeDivider':true,
    'durationDisplay':true,
    'remainingTimeDisplay':false
}}, function() {
    console.log('播放器初始化完成');
});

图片描述

3、video.js样式修改

.video-js{ /* 给.video-js设置字体大小以统一各浏览器样式表现,因为video.js采用的是em单位 */
  font-size: 14px;
}
.video-js button{
  outline: none;
}
.video-js.vjs-fluid,
.video-js.vjs-16-9,
.video-js.vjs-4-3{ /* 视频占满容器高度 */
  height: 100%;
  background-color: #161616;
}
.vjs-poster{
  background-color: #161616;
}
.video-js .vjs-big-play-button{ /* 中间大的播放按钮 */
  font-size: 2.5em;
  line-height: 2.3em;
  height: 2.5em;
  width: 2.5em;
  -webkit-border-radius: 2.5em;
  -moz-border-radius: 2.5em;
  border-radius: 2.5em;
  background-color: rgba(115,133,159,.5);
  border-width: 0.12em;
  margin-top: -1.25em;
  margin-left: -1.75em;
}
.video-js.vjs-paused .vjs-big-play-button{ /* 视频暂停时显示播放按钮 */
  display: block;
}
.video-js.vjs-error .vjs-big-play-button{ /* 视频加载出错时隐藏播放按钮 */
  display: none;
}
.vjs-loading-spinner { /* 加载圆圈 */
  font-size: 2.5em;
  width: 2em;
  height: 2em;
  border-radius: 1em;
  margin-top: -1em;
  margin-left: -1.5em;
}
.video-js .vjs-control-bar{ /* 控制条默认显示 */
  display: flex;
}
.video-js .vjs-time-control{display:block;}
.video-js .vjs-remaining-time{display: none;}

.vjs-button > .vjs-icon-placeholder:before{ /* 控制条所有图标,图标字体大小最好使用px单位,如果使用em,各浏览器表现可能会不大一样 */
  font-size: 22px;
  line-height: 1.9;
}
.video-js .vjs-playback-rate .vjs-playback-rate-value{
  line-height: 2.4;
  font-size: 18px;
}
/* 进度条背景色 */
.video-js .vjs-play-progress{
  color: #ffb845;
  background-color: #ffb845;
}
.video-js .vjs-progress-control .vjs-mouse-display{
  background-color: #ffb845;
}
.vjs-mouse-display .vjs-time-tooltip{
  padding-bottom: 6px;
  background-color: #ffb845;
}
.video-js .vjs-play-progress .vjs-time-tooltip{
  display: none!important;
}

4、动态切换视频

<script>
  var data = {
    src: 'xxx.mp4',
    type: 'video/mp4'
  };
  var player = videojs('myVideo', {...});
  player.pause();
  player.src(data);
  player.load(data);
  // 动态切换poster
  player.posterImage.setSrc('xxx.jpg');
  player.play();

  // 销毁videojs
  //player.dispose();
</script>

5、设置语言

5.1传统形式开发

对于使用<script>标签形式的方式引入video.js,只需要在页面中引入你需要的语言包即可

<script data-original="//example.com/path/to/lang/es.js"></script>
<script data-original="//example.com/path/to/lang/zh-CN.js"></script>
<script data-original="//example.com/path/to/lang/zh-TW.js"></script>

<script>
var player = videojs('myVideo', {
    language: 'zh-CN' // 初始化时设置语言,立即生效
});

/* 动态切换语言
  使用这种方式进行动态切换不会立即生效,必须有所操作后才会生效。如播放按钮,必须点击一次播放按钮后播放按钮的提示文字才会改变  
 */
//player.language('zh-TW');
</script>

5.2、vue开发

import Video from 'video.js'
/* 不能直接引入js,否则会报错:videojs is not defined 
import 'video.js/dist/lang/zh-CN.js' */
import video_zhCN from 'video.js/dist/lang/zh-CN.json'
import video_en from  'video.js/dist/lang/en.json'
import 'video.js/dist/video-js.css'

Video.addLanguage('zh-CN', video_zhCN);
Video.addLanguage('en', video_en);

6、解决在iPhone中播放时自动全屏问题(2019.09.23)

在iPhone设备上播放视频时(微信浏览器上也会有这个问题)会自动全屏,这里的全屏并不是常规的手机横屏那种全屏,而是类似于一个modal弹窗的全屏,解决办法就是在video标签中添加playsinline="true"属性

<video
    webkit-playsinline="true"
    playsinline="true"
    class="video-js vjs-big-play-centered vjs-fluid">
</video>

7、未解决的问题

控制条的高级自定义,如图中的进度条及时间在上面,播放按钮、上一个视频、下一个视频,设置及音量在下面这种控件该如何实现?
图片描述

如有知道实现这种高级自定义控制条方式的大神请在评论区留下您的代码

8、参考文章

查看原文

名是个代号 回答了问题 · 3月18日

关于element步骤条的步骤顺序错乱的问题

代码都不贴下吗

关注 3 回答 1

名是个代号 回答了问题 · 3月18日

vue怎么用watch实现无限树形,递归选中反选,如下图和代码

简单一点的话,你可以引入ui库,用组件,简单暴力。安利下element-ui和ivew的tree组件,也就是你所要的需求。https://element.eleme.cn/#/zh...
当然也是可以自己完善的,我也做过类似的,你可以参考下下面代码的思路,比如递归思路

      //点击复选框
      clickBox(data){
        var checkedNodesArr = this.$refs.tree.getCheckedNodes();
        let state = !!checkedNodesArr.find(item => item.id == data.id);
        // 处理点击含有子菜单的复选框时,把子菜单一起勾选或者一起取消勾选状态
        this.clickTreeData(data, state);
        // 处理点击子菜单时,若勾选时其父级以及祖级也要显勾选状态,不勾选即不用处理
        if (!!checkedNodesArr.find(item => item.id == data.id)) {
          this.checkTreeParents(data.pId);
        }
      },
      clickTreeData(data, state){
        let arr= this.getArr(data);
        //获取已勾选的所有数组
        var checkedNodesArr = this.$refs.tree.getCheckedNodes();
        arr.forEach((item)=>{
          if (state == false && checkedNodesArr.find(v => v.id == item.id)) {
            //若本来就在勾选的数组里,就将其移除
            checkedNodesArr.splice(checkedNodesArr.findIndex(v => v.id == item.id), 1);
          } else if (state == true && !checkedNodesArr.find(v => v.id == item.id)) {
            checkedNodesArr.push(item);
          }
        })
        this.$refs.tree.setCheckedNodes(checkedNodesArr);
      },
      getArr(obj){
        return [
          obj,
          ...obj.children.map(item => this.getArr(item)),
        ].flat(Infinity);
      },
      checkTreeParents(pid){
        let retArr = this.originalData.find(v => v.id == pid);
        var checkedNodesArr = this.$refs.tree.getCheckedNodes();
        if (!!retArr && !checkedNodesArr.find(v => v.id == retArr.id)) {
          checkedNodesArr.push(retArr);
          this.$refs.tree.setCheckedNodes(checkedNodesArr);
          this.checkTreeParents(retArr.pId);
        }
      },

关注 2 回答 1

名是个代号 回答了问题 · 3月18日

el-select无法选中option,会报warn: infinite update loop?

贴下代码出来啊

关注 2 回答 1

名是个代号 提出了问题 · 3月18日

解决videoJs toDataURL对视频截图疑难问题

<div class="test_two_box">
  <video
      id="myVideo"
      class="video-js vjs-big-play-centered"
      crossOrigin="anonymous"
      style="width: 515px;height: 300px;"
  >
    <source
        data-original="//vjs.zencdn.net/v/oceans.mp4"
        type="video/mp4"
    >
  </video>
</div>
<van-row type="flex" justify="center">
  <van-col>
    <van-button type="primary" @click="captureImg">对{{'id:' + captureObj }}进行视频截图</van-button>
  </van-col>
</van-row>
<div>
  <div style="width:0px;height:0px;overflow:hidden;">
    <canvas id="canvas" width="515" height="300"></canvas>
  </div>
  <img :data-original="imgSrc" style="margin-top:5px;" />
</div>
  data(){
    return {
      imgSrc     : '', //图片路径
      captureObj : 'myVideo',
    };
  },
  mounted(){
    this.initVideo();
    this.initCapture();
  },
  methods    : {
    initVideo(){
      //初始化视频方法
      let myPlayer = this.$video(myVideo, {
        //确定播放器是否具有用户可以与之交互的控件。没有控件,启动视频播放的唯一方法是使用autoplay属性或通过Player API。
        controls : true,
        //自动播放属性,muted:静音播放
        autoplay : 'false',
        //建议浏览器是否应在<video>加载元素后立即开始下载视频数据。
        preload  : 'auto',
        //设置视频播放器的显示宽度(以像素为单位)
        width    : '800px',
        //设置视频播放器的显示高度(以像素为单位)
        height   : '400px',
        poster   : 'https://dss0.baidu.com/73F1bjeh1BF3odCf/it/u=2304790197,3604046591&fm=85&s=E8F030C0C30F114B56BCFD82030070C3',
      });
    },
    initCapture(){
      this.videoTag = document.getElementById(this.captureObj);
      this.canvas = document.getElementById('canvas');
      this.context = this.canvas.getContext('2d');
      console.log('加载数据2:', this.videoTag, this.canvas, this.context);
    },
    //截图
    //注意 video标签设置crossOrigin属性为'anonymous'即可防止报跨域错误若报’toDataURL‘
    captureImg(){
      // 报错Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
      // 该方法可以正常截取到普通video
      // this.context.drawImage(this.videoTag, 0, 0, 515, 300);
      /*由于报错,根据网上找到的方法改进成下面方法,从而解决了报错。修改后截屏获取到的都是黑屏。普通的video也是黑屏*/
      this.canvas.onload = () => this.context.drawImage(this.videoTag, 0, 0, 515, 300);
      let base64 = this.canvas.toDataURL('image/jpeg');
      console.log('base64:', base64);
      this.imgSrc = base64;
    },
  },

我个人怀疑是这段代码的问题,若改回"this.context.drawImage(this.videoTag, 0, 0, 515, 300);",
就会报错"Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)';
依旧使用"this.canvas.onload = () => this.context.drawImage(this.videoTag, 0, 0, 515, 300);",截取到的始终是黑屏图片

  // 报错Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
  // 该方法可以正常截取到普通video
  // this.context.drawImage(this.videoTag, 0, 0, 515, 300);
  /*由于报错,根据网上找到的方法改进成下面方法,从而解决了报错。修改后截屏获取到的都是黑屏。普通的video也是黑屏*/
  this.canvas.onload = () => this.context.drawImage(this.videoTag, 0, 0, 515, 300);

关注 7 回答 6

认证与成就

  • 获得 0 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 3月18日
个人主页被 99 人浏览