Iposter v2.0 通用文章海报插件纯Javascript 实现

发布于 / 教程 / 0 条评论

之前写过两个海报插件,但都是基于后端实现,比较吃服务器资源,实际使用起来不尽如人意,于是重新写了这个js版本,在客户端浏览器就解决了海报生成。
市面上已经有的有很多,但是看起来还不错的真少。
此版本除了在实现上有所变化,在实际的效果上也做了一些优化。

效果预览

新版:

旧版:
shareposter

js版本在于轻量级,所以使用起来也很简单,在heade前引入以下Javascript 。

核心代码


class NiceposterGenerator {
    
  constructor() {
    this.canvas = null;
    this.context = null;
    this.createBackground();
  }

  createBackground() {
    const canvas = document.createElement("canvas");
    canvas.id = "myCanvas";
    canvas.width = 760;
    canvas.height = 1150;
    this.canvas = canvas;
    this.context = this.canvas.getContext("2d");
  }

  drawRoundedImage(context, image, x, y, width, height, radius) {
    context.save();
    context.beginPath();
    context.moveTo(x + radius, y);
    context.lineTo(x + width - radius, y);
    context.quadraticCurveTo(x + width, y, x + width, y + radius);
    context.lineTo(x + width, y + height - radius);
    context.quadraticCurveTo(
      x + width,
      y + height,
      x + width - radius,
      y + height
    );
    context.lineTo(x + radius, y + height);
    context.quadraticCurveTo(x, y + height, x, y + height - radius);
    context.lineTo(x, y + radius);
    context.quadraticCurveTo(x, y, x + radius, y);
    context.closePath();
    context.clip();
    context.drawImage(image, x, y, width, height);
    context.restore();
  }
  
  truncateChineseString(str, maxLength){
  var len = 0;
  var result = '';
  for (var i = 0; i < str.length; i++) {
    var charCode = str.charCodeAt(i);
    if (charCode >= 0x4E00 && charCode <= 0x9FFF) {
      len += 2;
    } else {
      len += 1;
    }
    if (len > maxLength) {
      break;
    }
    
    result += str.charAt(i);
  }
  
  return result;
}

  
  generate(meta){
    meta.font = meta.font ? meta.font : "SimSun";
    var title = this.truncateChineseString(meta.title,28);
    var desc = this.truncateChineseString(meta.desc,150);
    var authorName = this.truncateChineseString(meta.authorName,30);
    var context = this.context;
    context.fillStyle = "white";
    context.fillRect(0, 0, this.canvas.width, this.canvas.height);
    var img = new Image();
    img.crossOrigin = "Anonymous";
    img.src = meta.background;
    img.onload = () => {
      var imgCanvas = document.createElement("canvas");
      var imgContext = imgCanvas.getContext("2d");
      imgCanvas.width = this.canvas.width - 100;
      imgCanvas.height = meta.backgroundHeight;
      var cornerRadius = 40;
      this.drawRoundedImage(
        imgContext,
        img,
        0,
        0,
        imgCanvas.width,
        imgCanvas.height,
        cornerRadius
      );
      context.drawImage(imgCanvas, 50, 50);
      var date = new Date();
      var day = date.getDate();
      var month = date.getMonth() + 1;
      var year = date.getFullYear();
      context.font = "300 150px " + meta.font;
      context.fillStyle = "white";
      context.fillText(day, 90, 250);
      context.font = "36px " + meta.font;
      context.fillStyle = "white";
      context.fillText(month + "/" + year, 115, 320);
      context.font = "40px " + meta.font;
      context.fillStyle = "white";
      context.fillText(title, 100, 650 - 60);
      context.font = "30px " + meta.font;
      context.fillStyle = "white";
      context.fillText(authorName, 180, 840);
      var cardWidth = this.canvas.width - 100;
      var cardHeight = 180;
      var cardX = 50;
      var cardY = 930;
      context.save();
      context.beginPath();
      context.moveTo(cardX + cornerRadius, cardY);
      context.lineTo(cardX + cardWidth - cornerRadius, cardY);
      context.quadraticCurveTo(
        cardX + cardWidth,
        cardY,
        cardX + cardWidth,
        cardY + cornerRadius
      );
      context.lineTo(cardX + cardWidth, cardY + cardHeight - cornerRadius);
      context.quadraticCurveTo(
        cardX + cardWidth,
        cardY + cardHeight,
        cardX + cardWidth - cornerRadius,
        cardY + cardHeight
      );
      context.lineTo(cardX + cornerRadius, cardY + cardHeight);
      context.quadraticCurveTo(
        cardX,
        cardY + cardHeight,
        cardX,
        cardY + cardHeight - cornerRadius
      );
      context.lineTo(cardX, cardY + cornerRadius);
      context.quadraticCurveTo(cardX, cardY, cardX + cornerRadius, cardY);
      context.closePath();
      context.fillStyle = "rgb(230, 230,230)";
      context.fill();
      context.restore();
      var circularImg = new Image();
      circularImg.crossOrigin = "Anonymous";
      circularImg.src = meta.authorAvatar;
      circularImg.onload = function () {
        var circleDiameter = meta.avatarWidth;
        var circleX = 100;
        var circleY = 800;
        var circleRadius = circleDiameter / 2;
        context.save();
        context.beginPath();
        context.arc(
          circleX + circleRadius,
          circleY + circleRadius,
          circleRadius,
          0,
          2 * Math.PI
        );
        context.closePath();
        context.clip();
        context.drawImage(
          circularImg,
          circleX,
          circleY,
          circleDiameter,
          circleDiameter
        );
        context.restore();
      };
      context.font = "20px " + meta.font;
      context.fillStyle = "black";
      context.fillText(meta.footer, 100, 980);
      context.font = "18px " + meta.font;
      context.fillStyle = "rgb(120,120,120)";
      context.fillText(meta.slogen, 100, 1060);
      var qrCode = new QRCode(document.createElement("div"), {
        text: meta.targetQrcodeUrl,
        width: 120,
        height: 120
      });
      var qrCodeImage = qrCode._el.children[0].toDataURL("image/png");
      var qr_x = this.canvas.width - 100 - 120;
      var qr_y = cardY + 30;
      var qrCodeImgElement = new Image();
      qrCodeImgElement.src = qrCodeImage;
      qrCodeImgElement.onload = () => {
        cornerRadius = 20;
        context.save();
        context.beginPath();
        context.moveTo(qr_x + cornerRadius, qr_y);
        context.lineTo(qr_x + 125 - cornerRadius, qr_y);
        context.quadraticCurveTo(
          qr_x + 125,
          qr_y,
          qr_x + 125,
          qr_y + cornerRadius
        );
        context.lineTo(qr_x + 125, qr_y + 125 - cornerRadius);
        context.quadraticCurveTo(
          qr_x + 125,
          qr_y + 125,
          qr_x + 125 - cornerRadius,
          qr_y + 125
        );
        context.lineTo(qr_x + cornerRadius, qr_y + 125);
        context.quadraticCurveTo(
          qr_x,
          qr_y + 125,
          qr_x,
          qr_y + 125 - cornerRadius
        );
        context.lineTo(qr_x, qr_y + cornerRadius);
        context.quadraticCurveTo(qr_x, qr_y, qr_x + cornerRadius, qr_y);
        context.closePath();
        context.fillStyle = "white";
        context.fill();
        context.restore();
        this.drawRoundedImage(
          context,
          qrCodeImgElement,
          qr_x,
          qr_y,
          125,
          125,
          cornerRadius
        );
      };
      var chineseText = desc+'...';
      var lines = [];
      var words = chineseText.split("");
      var currentLine = words[0];
      var lineHeight = 40;
      var maxWidth = 560;
      context.font = "20px " + meta.font;
      context.fillStyle = "white";
      var x = 100;
      var y = 720 - 60;
      for (var i = 1; i < words.length; i++) {
        var word = words[i];
        var testLine = currentLine + word;
        var metrics = context.measureText(testLine);
        var testWidth = metrics.width;
        if (testWidth > maxWidth) {
          lines.push(currentLine);
          currentLine = word;
        } else {
          currentLine = testLine;
        }
      }
      lines.push(currentLine);
      for (var j = 0; j < lines.length; j++) {
        context.fillText(lines[j], x, y);
        y += lineHeight;
      }
    };
  }
  
}

使用

引入qrcode库

/qrcode.min.js

Javascript


 //

 const meta = 
       {
        id:1111,
        title:"通用文章海报Javascript插件",
        desc:"iPoster是一款通用的文章精美海报生成的javascript插件...",
        authorName:"LYLARES",
        authorAvatar:"https://www.lylares.com/wp-content/uploads/2017/02/30845bc72dceb15689126805931d636c-2.png",
        footer:"LYLARES BLOG",
        slogen:"望眼相看,似是故人来。",
        font:"iconfont",
        avatarWidth:60,
        background:"https://www.lylares.com/wp-content/uploads/2017/02/dbc4b303c0ca21c10d6bdb4a4ecb7616.jpeg",
        backgroundHeight:850,
        targetQrcodeUrl:"https://www.todaybing.com/detail/YLsUhviX.html",
        posterWidth:760,
        posterHeight:1150
        };

iposterGenerator.generate(meta);
var src = iposterGenerator.canvas.toDataURL("image/png");//生成的海报图片

参数说明


id:暂时无用
title:自定义标题
desc:自定义描述,会自动截断字符不超过三行。
authorName:作者名称
authorAvatar:非必需项,不为空将在左下角展示作者头像,位置固定,受到背景图高度影响
footer:footer
slogen:slogen
font:字体,默认与网页一致
avatarWidth:作者头像宽度
background:背景图地址
backgroundHeight:背景图高度
targetQrcodeUrl:二维码目标地址
posterWidth:海报宽
posterHeight:海报高


背景图高度会影响整个海报的观感,下个版本将加入自动调整。

以前的后端版本可以参考之前的文章:

shareposter-WordPress文章海报生成插件

shareposter是一款为wordpress文章自动生成分享海报的插件 shareposter是一款能自动给文章生成一张分辨率为750*1366海报的插件,海报样式精美且自带文章链接 ...
https://www.lylares.com/shareposter.html


定制开发

接受各种定制开发,微信咨询:

前往查看

赞助支持

转载原创文章请注明,转载自: LYLARES BLOG » Iposter v2.0 通用文章海报插件纯Javascript 实现

Not Comment Found