vue实现微信对话

因为项目中需要实现仿微信对话功能,于是抽空实现了下,主要是h5的canvas的把图片和文字绘制到画布上

原文来自我的个人博客:http://lvhww.com/index.php/archives/6/

1.首先准备图片资源:微信头部导航,对话框,二维码等

 

 2.API接口

3,效果图

 

4,代码DetailWechat.vue

<template>
  <div class="s-all">
    <v-header :name="name"></v-header>
    <!-- 人物列表 -->
    <div class="s-content active">
      <div class="s-recommend">
        <span class="span-1">荐</span>
        <span>推荐人物</span></div>
      <div class="s-recommend-people">
        <ul>
          <li v-for="item in recommendList">
            <div class="s-img">
              <img @click="drawResult(item.name,item.nickname,item._id,$event)"
                   src="../assets/images/e217d58ec3d03b1ab5df0d92589a04bb.jpg"/>
              <!--:src="item.image.replace('/game/static/images/','')"/>-->
            </div>
            <div class="s-title" v-text="item.name">
            </div>
          </li>
        </ul>
      </div>
      <div class="s-category">
        <span class="span-2">类</span>
        <span>分类</span>
      </div>
      <div class="s-category-a">
        <a class="s-bt" @click="getData(Aname.name)" v-for="Aname in Alist">{{Aname.name}}</a>
      </div>
      <div class="s-category-list">
        <ul>
          <li v-for="item in itemList">
            <a class="s-a-1" @click="drawResult(item.name,item.name,item._id)" :data-alt="item.name"
               :data-nickname="item.name" :data-num="item._id" name="s-a-1">
              <div class="s-a-img">
                <img v-lazy="item.image" class="star-icon" id="star-icon"/>
              </div>
              <div class="s-a-title">
                <div class="s-a-title-name" v-text="item.name"></div>
                <div class="s-a-title-play">
                  <span class="m-icon icon-star"></span>
                  <span class="m-icon icon-star"></span>
                  <span class="m-icon icon-star"></span>
                  <span class="m-icon icon-star"></span>
                  <span class="m-icon icon-star"></span>
                  <span class="s-people">123456人在玩</span>
                </div>
              </div>
            </a>
            <a class="s-a-2" @click="drawResult(item.name,item.name,item._id,$event)" :data-alt="item.name"
               :data-nickname="item.name" :data-num="item._id">
              开始<span class="icon-more"></span>
            </a>
          </li>
        </ul>
      </div>
    </div>
    <!-- 正在制作 -->
    <div class="box" id="page2">
      <img id="loading" class="loading" src="https://h5.tangdaoya.com/game/static/loading.png"/>
      <p id="loading-text" class="loading-text">正在生成</p>
    </div>
    <!-- 制作结果 -->
    <div class="box" id="page3">
      <div class="common-result-btnbox">
        <div class="commonbtn-playAgain btn-back" @click="changeContent()" v-if="ShowAgain">
          换个文案试试
        </div>
        <div id="btn-share" class="commonbtn-share">立即去整人</div>
      </div>
      <div class="resultimgbox">
        <img id="result" class="result" src=""/>
      </div>
    </div>
    <!-- 返回制作 -->
    <div class="box" id="page4">
      <div class="butbox common-result-btnbox">
        <div id="backtohomgpage" class="commonbtn-share">返回制作</div>
      </div>
    </div>
    <div class="" style="display: block">
      <img src="../assets/images/header.jpg" id="headerImg" alt="">
      <img src="../assets/images/footer.jpg" id="footerImg" alt="">
      <img src="../assets/images/qr.png" id="codeImg" alt="">
      <img src="../assets/images/8fe39669bff9423bb56c5586f5fa70d1.jpg" id="head2Img" alt="">
    </div>
    <img src="" id="targetImg" alt="">
  </div>
</template>
<script>
  import Vue from 'vue'
  import api from '../fetch/api.js'
  import com from '../assets/js/common'
  import axios from 'axios';
  import VueLazyload from 'vue-lazyload'
  import {Toast} from 'mint-ui'
  import VHeader from '@/components/Header.vue'
  var headImg = "" || "https://h5.tangdaoya.com/zb/static/image/cover/8fe39669bff9423bb56c5586f5fa70d1.jpg";
  var manifest = [{
    src: "https://h5.tangdaoya.com/game/static/header.jpg",
    id: "header"
  }, {
    src: "https://h5.tangdaoya.com/game/static/footer.jpg",
    id: "footer"
  }, {
    src: headImg,
    id: "head2"
  }];
  var headerEle;
  var footerEle;
  var codeEle;
  var head2Ele;
  var starImgObg;
  var chatContent, template;
  var starName = "张信哲", starNickName = "張信哲JeffChang", starID = 1, starImg;
  var cW = 640, cHeigth = 1138, headL = 18, headW = 63, headR = cW - headL - headW,
    padding = 20, fontSize = 23, lingheight = 30, imgTextSpace = 20,
    starTextL = headL + headW + imgTextSpace - 3,
    starTextR = headR - imgTextSpace,
    maxTextW = 405, gY = 0, verticlaSpace = 34, textRadius = 6;
  CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
    if (w < 2 * r) r = w / 2;
    if (h < 2 * r) r = h / 2;
    this.beginPath();
    this.moveTo(x + r, y);
    this.arcTo(x + w, y, x + w, y + h, r);
    this.arcTo(x + w, y + h, x, y + h, r);
    this.arcTo(x, y + h, x, y, r);
    this.arcTo(x, y, x + w, y, r);
    this.closePath();
    return this;
  }


  export default {
    components: {
      VHeader
    },
    data() {
      return {
        name: '',
        itemList: [],
        recommendList: [],
        Alist: [{
          name: '影视大咖'
        }, {
          name: '女明星'
        }, {
          name: '男明星'
        }, {
          name: '商业大亨'
        }],
        starName: '',
        starNickName: '',
        starID: '',
        starImgObg: '',
        chatContent: '',
        template: '',
        ShowAgain: false
      }
    },
    created() {
      this.getData();
    },
    mounted() {
      let self = this;
      headerEle = document.getElementById('headerImg');
      footerEle = document.getElementById('footerImg');
      codeEle = document.getElementById('codeImg');
      head2Ele = document.getElementById('head2Img');
      CanvasRenderingContext2D.prototype.drawStarText = function (x, y, r, text, translate) {
        self.drawStarImg(this, y);
        console.log('star')
        this.font = "24px Helvetica";
        var hastrans = false;
        var wordCnt = com.isChineseChar(text) ? 32 : 33;
        console.log(wordCnt)
        var textArr = self.getText2Arr(this, text, wordCnt);
        var h = headW + lingheight * (textArr.length - 1);
        var w = (textArr.length > 1) ? maxTextW : (this.measureText(textArr[0]).width + 2 * padding);
        if (translate) {
          hastrans = true;
          var transArr = self.getText2Arr(this, translate, 32);
          h = h + lingheight * (transArr.length) + 48;
          if (transArr.length > 1) {
            w = maxTextW;
          }
        }
        gY = y + h + verticlaSpace;
        if (w < 2 * r) r = w / 2;
        if (h < 2 * r) r = h / 2;
        this.save();
        this.beginPath();
        this.moveTo(x + r, y);
        this.arcTo(x + w, y, x + w, y + h, r);
        this.arcTo(x + w, y + h, x, y + h, r);
        this.arcTo(x, y + h, x, y, r);

        this.lineTo(x, y + 31 + 7);
        this.lineTo(x - 10, y + 31);
        this.lineTo(x, y + 31 - 7);
        this.arcTo(x, y, x + w, y, r);
        this.closePath();
        this.strokeStyle = '#d3d3d3';
        this.lineWidth = 2;
        this.stroke();
        this.fillStyle = "#fff";
        this.fill();

        this.textBaseline = 'top';
        this.textAlign = 'left';
        this.fillStyle = '#000';
        self.writeTextArr(this, textArr, lingheight, x + padding, y + padding);
        //writeMulLineText(this, text, 32, 30, x + padding, y+padding);
        //this.fillText(text, x + padding, y+padding);
        if (hastrans) {
          this.beginPath();
          var lineY = y + padding * 2 + textArr.length * lingheight;
          this.moveTo(x + padding, lineY);
          this.lineTo(x + w - padding, lineY);
          this.strokeStyle = "#ddd";
          this.stroke();
          self.writeTextArr(this, transArr, lingheight, x + padding, lineY + 20);
          gY = gY - verticlaSpace;
          this.transFlag(x, gY + 8, textRadius);
        }
        this.restore();

        return this;
      }

      CanvasRenderingContext2D.prototype.drawSelfText = function (x, y, r, text) {
        self.drawSelfImg(this, y);
        this.font = "24px Helvetica"; // Arial
        var wordCnt = com.isChineseChar(text) ? 32 : 33;
        var textArr = self.getText2Arr(this, text, wordCnt);
        var h = headW + lingheight * (textArr.length - 1);
        var w = (textArr.length > 1) ? maxTextW : (this.measureText(textArr[0]).width + 2 * padding);
        x = x - w;
        gY = y + h + verticlaSpace;
        if (w < 2 * r) r = w / 2;
        if (h < 2 * r) r = h / 2;
        this.save();
        this.beginPath();
        this.moveTo(x + r, y);

        this.arcTo(x + w, y, x + w, y + h, r);
        this.lineTo(x + w, y + 31 + 7);
        this.lineTo(x + w + 10, y + 31);
        this.lineTo(x + w, y + 31 - 7);

        this.arcTo(x + w, y + h, x, y + h, r);
        this.arcTo(x, y + h, x, y, r);
        this.arcTo(x, y, x + w, y, r);
        this.closePath();

        this.strokeStyle = '#8bdf49';
        this.lineWidth = 2;
        this.stroke();
        this.fillStyle = "#a0e75a";
        this.fill();

        this.textBaseline = 'top';
        this.textAlign = 'left';
        this.fillStyle = '#000';
        self.writeTextArr(this, textArr, lingheight, x + padding, y + padding);
        //writeMulLineText(this, text, 32, 30, x + padding, y+padding);
        //this.fillText(text, x + padding, y+padding);
        this.restore();

        return this;
      }

      CanvasRenderingContext2D.prototype.drawHelloText = function (x, y, r, text) {
        this.save();
        this.font = "18px Helvetica";
        var h = 32;
        var w = this.measureText(text).width + 2 * padding;
        x = x - w / 2;
        gY = y + h + 22;
        if (w < 2 * r) r = w / 2;
        if (h < 2 * r) r = h / 2;

        this.beginPath();
        this.moveTo(x + r, y);
        this.arcTo(x + w, y, x + w, y + h, r);
        this.arcTo(x + w, y + h, x, y + h, r);
        this.arcTo(x, y + h, x, y, r);
        this.arcTo(x, y, x + w, y, r);
        this.closePath();
        this.fillStyle = "#cecece";
        this.fill();
        this.textBaseline = 'middle';
        this.textAlign = 'center';
        this.fillStyle = '#fff';
        this.fillText(text, cW / 2, y + h / 2);
        this.restore();

        return this;
      }

      CanvasRenderingContext2D.prototype.transFlag = function (x, y, r, text) {
        this.save();
        this.font = "18px Helvetica";
        var h = 32;
        text = '√ 已翻译';
        var transpading = 10;
        var w = this.measureText(text).width + 2 * transpading;
        gY = y + h + verticlaSpace;
        if (w < 2 * r) r = w / 2;
        if (h < 2 * r) r = h / 2;
        this.beginPath();
        this.moveTo(x + r, y);
        this.arcTo(x + w, y, x + w, y + h, r);
        this.arcTo(x + w, y + h, x, y + h, r);
        this.arcTo(x, y + h, x, y, r);
        this.arcTo(x, y, x + w, y, r);
        this.closePath();
        this.fillStyle = "#cecece";
        this.fill();
        this.textBaseline = 'middle';
        this.textAlign = 'left';
        this.fillStyle = '#fff';
        this.fillText(text, x + transpading, y + h / 2);
        this.restore();
        return this;
      }
      CanvasRenderingContext2D.prototype.drawQrcode = function (img, x, y) {
        self.drawStarImg(this, y);
        var h = 200 + padding;
        var w = 200 + padding;
        var r = textRadius;
        if (w < 2 * r) r = w / 2;
        if (h < 2 * r) r = h / 2;
        this.save();
        this.beginPath();
        this.moveTo(x + r, y);
        this.arcTo(x + w, y, x + w, y + h, r);
        this.arcTo(x + w, y + h, x, y + h, r);
        this.arcTo(x, y + h, x, y, r);

        this.lineTo(x, y + 31 + 7);
        this.lineTo(x - 10, y + 31);
        this.lineTo(x, y + 31 - 7);
        this.arcTo(x, y, x + w, y, r);
        this.closePath();
        this.strokeStyle = '#d3d3d3';
        this.lineWidth = 2;
        this.stroke();
        this.fillStyle = "#fff";
        this.fill();
        this.drawImage(img, x + 10, y + 10, 200, 200)
        gY = y + h + verticlaSpace;
      }
    },
    methods: {
      getData(ev) {
        let self = this;
        // 默认页面加载的数据
        if (ev == undefined) {
          self.$http.get('https://h5.tangdaoya.com/game/wechat/getstarbytype/影视大咖')
            .then(res => {
              let result = res.data.list;
//              self.itemList = result;
              self.recommendList = result;
            }).catch(error => {
            Toast('网络出现错误,请稍后再试');
          });

        } else {
          // 传递参数请求接口
          self.$http.get('https://h5.tangdaoya.com/game/wechat/getstarbytype/' + ev)
            .then(res => {
              console.log(res.data.list);
              let result = res.data.list;
              self.itemList = result;
            }).catch(error => {
            Toast('网络出现错误,请稍后再试');
          });
        }
      },
      drawResult(a, b, c, e) {
        let self = this;
        self.starName = a;
        self.starNickName = b;
        self.starID = c;
        starImgObg = e.target;
        Toast('正在生成中~');
        self.getChatContent();
      },
      gameStart() {
        let self = this;
        var canvas = document.createElement('canvas');
        canvas.width = cW;
        canvas.height = cHeigth;
        var context = canvas.getContext('2d');
        context.fillStyle = '#ebebeb';
        context.fillRect(0, 0, cW, cHeigth);
        context.drawImage(headerEle, 0, 0);
        self.writeHeader(context, starName);
        gY = 115;

        if (template == 1) {
          context.drawHelloText(cW / 2, gY, textRadius, com.getTime(true));
        } else {
          context.drawHelloText(cW / 2, gY, textRadius, com.getDateTime());
        }

        for (var i = 0; i < chatContent.length; i++) {
          var contentTemp = chatContent[i]['content'];
          if (com.is_weixn()) {
            contentTemp = contentTemp.replace(/xxx/ig, nickName);
          } else {
            contentTemp = contentTemp.replace(/是xxx吗?/ig, '');
          }
          if (chatContent[i] && chatContent[i]['star']) {
            if (chatContent[i]['isImg']) {
              // context.drawQrcode(codeEle, starTextL, gY);
              context.drawQrcode(codeEle, starTextL, gY);

            } else {
              context.drawStarText(starTextL, gY, textRadius, contentTemp, chatContent[i]['translate']);
              if (i == 0 && template == 2) {
                self.drawHello(context, starNickName);
                gY = gY + 12;
              }
            }
          } else {
            context.drawSelfText(starTextR, gY, textRadius, contentTemp);
          }
        }
        // context.drawImage(footerEle, 0, cHeigth - 78, 640, 78);
        context.drawImage(footerEle, 0, cHeigth - 78, 640, 78);
        console.log('canvas'+canvas);
        console.log(canvas);
          console.log(canvas.toDataURL("image/png", 0.6));
        self.saveImage(canvas.toDataURL("image/png", 0.6))
      },
      saveImage(data) {
        $('#targetImg').attr('src', data);
      },
      getChatContent() {
        let self = this;
        var server = "https://h5.tangdaoya.com/game/wechat/getChatContent/" + 2;//this.starID
        self.$http.get(server).then(res => {
          let result = res.data;
          this.chatContent = result.chatContents;
          chatContent = this.chatContent;
          this.template = result['template'];
          if (result['changeContent'] == 0) {
            //Selector('.commonbtn-playAgain').style.display = "none";
            this.ShowAgain = false;
          } else if (result['changeContent'] == 1) {
            this.ShowAgain = true;
          }
          self.gameStart();
        }).catch(error => {
          //Toast('网络出现错误,请稍后再试');
        });


      },
      changeContent() {
        $('#result').attr('src', '');
        $('#page2').removeClass('active');
        $('#page3').addClass('active');
      },
      drawStarImg(ctx, y) {
        ctx.drawImage(starImgObg, headL, y, headW, headW);
      },
      writeHeader(ctx, name) {
        var y = 67;
        ctx.save();
        ctx.textBaseline = 'middle';
        ctx.textAlign = 'center';
        ctx.fillStyle = '#fff';
        ctx.font = "19px Arial";
        ctx.fillText(com.getTime(), cW / 2, 18);
        ctx.font = "29px Arial";
        ctx.fillText(name, cW / 2, y);
        ctx.restore();
      },
      drawSelfImg(ctx, y) {
        //ctx.drawImage(selfImgObg, headR, y, headW, headW)    ;
        // ctx.drawImage(head2Ele, headR, y, headW, headW);
        ctx.drawImage(head2Ele, headR, y, headW, headW);
      },
      getText2Arr(ctx, text, rw) {
        var textArr = [];
        for (var i = 0; com.getTrueLength(text) > 0; i++) {
          var tl = com.cutString(text, rw);
          if (ctx.measureText(text.substr(0, tl)).width < 350)
            tl = tl + 1;
          for (var j = 0; j < tl; j++) {
            var temp = text.substr(tl, 1);
            if (temp == " " || temp.charCodeAt(0) > 128 || temp == "") {
              break;
            }
            else {
              tl = tl - 1;
            }
          }
          //ctx.fillText(text.substr(0, tl).replace(/^\s+|\s+$/, ""), offsetX , i * lineheight + offsetY);
          textArr.push(text.substr(0, tl));
          text = text.substr(tl);
        }

        return textArr;
      },
      writeTextArr(ctx, textArr, lineheight, offsetX, offsetY) {
        for (var i = 0; i < textArr.length; i++) {
          ctx.fillText(textArr[i].replace(/^\s+|\s+$/, ""), offsetX, i * lineheight + offsetY);
        }
      }
    }
  }
</script>
<style lang="less" rel="stylesheet/less" scoped>
  .s-content {
    margin-top: 50px;
    height: 1000px;
  }

  .s-recommend {
    height: 30px;
    line-height: 2em;
    border-bottom: 1px solid #F0F0F7;
    .span-1 {
      background-color: #FF4852;
      color: white;
      margin-left: 10px;
    }
  }

  .s-recommend-people {
    width: 100%;
    height: 100px;
    margin-top: 5px;
    border-bottom: 3px solid #F0F0F7;
    ul {
      width: 100%;
      height: 100px;
      margin: 0 auto;
      li {
        position: relative;
        display: inline-block;
        list-style: none;
        width: 25%;
        height: 80px;
        margin-top: 10px;
        margin-left: 6%;
        .s-img {
          width: 50px;
          height: 50px;
          margin: 0 auto;
          img {
            width: 50px;
            height: 50px;
            border-radius: 10px;
          }
        }
        .s-title {
          width: 50px;
          height: 20px;
          border: 1px solid balck;
          margin: 0 auto;
          text-align: center;
          margin-top: 5px;
          font-size: 0.8em;
        }
      }
    }
  }

  .s-category {
    height: 30px;
    line-height: 2em;
    border-bottom: 1px solid #F0F0F7;
    .span-2 {
      background-color: #139F00;
      color: white;
      margin-left: 10px;
    }
  }

  .s-category-a {
    height: 50px;
    margin-top: 5px;
    a {
      border: 1px solid #9b9b9b;
      display: inline-block;
      border-radius: 10px;
      padding: .15rem 0.31rem;
      display: inline-block;
      margin-top: 12px;
      height: 25px;
      line-height: 1.6em;
      margin-left: 10px;
    }
  }

  .s-category-list {
    margin-top: 10px;
    height: 700px;
    ul {
      width: 100%;
      height: 100%;
      li {
        border-bottom: 1px solid #F0F0F7;
        width: 100%;
        height: 80px;
        margin-top: 10px;
        .s-a-1 {
          float: left;
          width: 78%;
          height: 80px;
          .s-a-img {
            float: left;
            width: 60px;
            height: 60px;
            background-color: gray;
            margin-top: 10px;
            margin-left: 10px;
            border-radius: 10px;
            img {
              width: 60px;
              height: 60px;
              border-radius: 10px;
            }
          }
          .s-a-title {
            float: left;
            height: 60px;
            margin-top: 10px;
            margin-left: 5px;
            .s-a-title-name {
              margin-top: 10px;
              color: black;
              font-size: 0.8em;
            }
            .s-a-title-play {
              margin-top: 10px;
              .s-people {
                font-size: 0.8em;
              }
              .icon-star {
                font-size: 0.9em;
              }
            }
          }
        }
        .s-a-2 {
          float: right;
          width: 60px;
          height: 27px;
          border: 1px solid #EFB31D;
          margin-top: 25px;
          margin-right: 20px;
          border-radius: 5px;
          line-height: 1.8em;
          text-align: center;
          color: #EFB31D;
          .icon-more {
            display: inline-block;

            color: #EFB31D;

          }
        }
      }
    }
  }

  /* 制作页样式*/

  .box {
    display: none;
    .loading {
      position: relative;
      width: 75px;
      margin-top: 40%;
    }
    .loading-text {
      position: relative;
      text-align: center;
      color: #FF4303;
      margin-top: 20px auto;
      font-family: "微软雅黑";
      letter-spacing: 1px;
    }
  }

  .commonbtn-share {
    display: inline-block;
    margin: 0.5%;
    padding: 0.5em 0em;
    font-size: 16px;
    color: white;
    background-color: #FF4303;
    border-radius: 40px;
    width: 35%;
    box-sizing: border-box;
    -webkit-box-sizing: border-box;
  }

  .active .loading {
    -webkit-animation: roting 1s linner infinite;
  }
</style>

 

posted @ 2017-05-07 21:14  桃之夭夭丶  阅读(1178)  评论(0编辑  收藏  举报