PIX优化分享生成海报样式(底部LOGO/描述/二维码)

pix主题分享生成海报的JS位于inc/assets/js/poster.js,代码如下:

// 部分代码来自ZIBILL主题作者
// canvas生成海报
const poster = (function() {

  const DEBUG = false

  const WIDTH = 700
  const HEIGHT = 1160

  function init(config) {
    const $container = document.querySelector(config.selector)
    $container.style.border = '1px solid #f0f0f0'
    const $wrapper = createDom('div', 'id', 'wrapper')
    const $canvas = createDom('canvas', 'id', 'canvas', 'block')
    const $day = createDom('canvas', 'id', 'day')
    const $date = createDom('canvas', 'id', 'date')
    const $title = createDom('canvas', 'id', 'title')
    const $content = createDom('canvas', 'id', 'content')
    const $logo = createDom('canvas', 'id', 'logo')
    const $description = createDom('canvas', 'id', 'description')

    appendChilds($wrapper, $canvas, $day, $date, $title, $content, $logo, $description)
    $container.appendChild($wrapper)

    const date = new Date()

    // day
    const dayStyle = {
      font: 'bold 80px Helvetica',
      color: 'rgba(255, 255, 255, 1)',
      position: 'right'
    }
    drawOneline($day, dayStyle, date.getDate());

    // date
    const dateStyle = {
      font: '23px Helvetica',
      color: 'rgba(255, 255, 255, 1)',
      position: 'right'
    }
    drawOneline($date, dateStyle, date.getFullYear() + '/' + (date.getMonth() + 1))

    // title canvas
    const titleStyle = {
      font: '30px Helvetica',
      lineHeight: 1.5,
      color: 'rgba(255, 255, 255, 1)',
      length: 2,
      position: 'left'
    }
    titleStyle.font = (config.titleStyle && config.titleStyle.font) || titleStyle.font
    titleStyle.color = (config.titleStyle && config.titleStyle.color) || titleStyle.color
    titleStyle.position = (config.titleStyle && config.titleStyle.position) || titleStyle.position
    drawMoreLines($title, titleStyle, config.title)

    // content canvas
    const contentStyle = {
      font: '22px Helvetica',
      lineHeight: 1.5,
      position: 'left',
      color: 'rgba(236, 241, 255, 1)'
    }
    contentStyle.font = (config.contentStyle && config.contentStyle.font) || contentStyle.font
    contentStyle.color = (config.contentStyle && config.contentStyle.color) || contentStyle.color
    contentStyle.lineHeight = (config.contentStyle && config.contentStyle.lineHeight) || contentStyle.lineHeight
    contentStyle.position = (config.contentStyle && config.contentStyle.position) || contentStyle.position
    drawMoreLines($content, contentStyle, config.content);

    // description
    const descriptionStyle = {
      font: '24px Helvetica',
      color: 'rgba(180, 180, 180, 1)',
      lineHeight: 1.2,
      position: 'left'
    }
    drawMoreLines($description, descriptionStyle, config.description)


    // background image
    const image = new Image();
    image.crossOrigin = "Anonymous";

    //logo
    const logo = new Image();
    logo.crossOrigin = "Anonymous";
    logo.src = config.logo;

    //qrcode
    const qrcode = new Image();
    qrcode.src = config.qrcode;


    const onload = function() {
      $canvas.width = WIDTH;
      $canvas.height = HEIGHT;
      image.src = config.banner;
      image.onload = function() {
        const ctx = $canvas.getContext('2d')
        ctx.fillStyle = 'rgba(255, 255, 255, 1)';
        ctx.fillRect(0, 0, $canvas.width, $canvas.height);
        
        // banner
        imgRect = coverImg($canvas.width - 40, $canvas.height / 1.2 - 40, image.width, image.height);
        ctx.drawImage(image, imgRect.sx, imgRect.sy, imgRect.sWidth, imgRect.sHeight, 20, 20, $canvas.width - 40, $canvas.height / 1.2 - 40);

        //覆盖层
        ctx.fillStyle="rgba(0, 0, 0, 0.3)";
        ctx.fillRect(20,20,$canvas.width - 40,$canvas.height / 1.2 - 40);        

        // 时间
        ctx.drawImage($day, -20, 50)
        ctx.drawImage($date, -21, 125)

        //logo
        var logowidth = logo.width;
        var logoheight = logo.height;
        var scale = logowidth / logoheight;
        var logoh = 200;
        var cwidth = logoh * scale;
        ctx.drawImage(logo, 70, $canvas.height / 1.2 - 20, cwidth, logoh);

        ctx.drawImage(qrcode, $canvas.width - 280, $canvas.height / 1.2 - 20, 220, 220);

        //标题文字
        ctx.drawImage($title, 20, $canvas.height / 2 + 20)
        ctx.drawImage($content, 20, $canvas.height / 2 + 120)
        ctx.drawImage($description, 20, $canvas.height / 1.2 + 170)
        ctx.strokeStyle = 'rgba(122, 122, 122, 0.5)';

        const img = new Image();
        img.crossOrigin = "Anonymous";
        img.src = $canvas.toDataURL('image/png')
        const radio = config.radio || 0.7
        img.width = WIDTH * radio
        img.height = HEIGHT * radio
        img.className = 'poster_load';
        ctx.clearRect(0, 0, $canvas.width, $canvas.height)
        $canvas.style.display = 'none'

        if ($container.querySelector('.poster_load')) {
          $container.querySelector('.poster_load').src = img.src;
        } else {
            $container.appendChild(img);
        }

        $container.appendChild(img);
        $container.removeChild($wrapper)
        if (config.callback) {
          config.callback($container)
        }
      }
    }

    onload()
  }

    //裁切
    function containImg(sx, sy, box_w, box_h, source_w, source_h) {
      var dx = sx,
          dy = sy,
          dWidth = box_w,
          dHeight = box_h;
      if (source_w > source_h || (source_w == source_h && box_w < box_h)) {
          dHeight = source_h * dWidth / source_w;
          dy = sy + (box_h - dHeight) / 2;

      } else if (source_w < source_h || (source_w == source_h && box_w > box_h)) {
          dWidth = source_w * dHeight / source_h;
          dx = sx + (box_w - dWidth) / 2;
      }
      return {
          dx,
          dy,
          dWidth,
          dHeight
      }
  }

function coverImg(box_w, box_h, source_w, source_h) {
    var sx = 0,
        sy = 0,
        sWidth = source_w,
        sHeight = source_h;
    if (source_w > source_h || (source_w == source_h && box_w < box_h)) {
        sWidth = box_w * sHeight / box_h;
        sx = (source_w - sWidth) / 2;
    } else if (source_w < source_h || (source_w == source_h && box_w > box_h)) {
        sHeight = box_h * sWidth / box_w;
        sy = (source_h - sHeight) / 2;
    }
    return {
        sx,
        sy,
        sWidth,
        sHeight
    }
}

  function createDom(name, key, value, display = 'none') {
    const $dom = document.createElement(name)
    $dom.setAttribute(key, value)
    $dom.style.display = display
    $dom.width = WIDTH
    return $dom
  }

  function appendChilds(parent, ...doms) {
    doms.forEach(dom => {
      parent.appendChild(dom)
    })
  }

  function drawOneline(canvas, style, content) {
    const ctx = canvas.getContext('2d')
    canvas.height = parseInt(style.font.match(/\d+/), 10) + 20
    ctx.font = style.font
    ctx.fillStyle = style.color
    ctx.textBaseline = 'top'

    let lineWidth = 0
    let idx = 0
    let truncated = false
    for (let i = 0; i < content.length; i++) {
      lineWidth += ctx.measureText(content[i]).width;
      if (lineWidth > canvas.width - 60) {
        truncated = true
        idx = i
        break
      }
    }

    let padding = 30

    if (truncated) {
      content = content.substring(0, idx)
      padding = canvas.width / 2 - lineWidth / 2
    }

    if (DEBUG) {
      ctx.strokeStyle = "#6fda92";
      ctx.strokeRect(0, 0, canvas.width, canvas.height);
    }

    if (style.position === 'center') {
      ctx.textAlign = 'center';
      ctx.fillText(content, canvas.width / 2, 0)
    } else if (style.position === 'left') {
      ctx.fillText(content, padding, 0)
    } else {
      ctx.textAlign = 'right'
      ctx.fillText(content, canvas.width - padding, 0)
    }
  }

  function drawMoreLines(canvas, style, content) {
    const ctx = canvas.getContext('2d')
    const fontHeight = parseInt(style.font.match(/\d+/), 10)

    if (DEBUG) {
      ctx.strokeStyle = "#6fda92";
      ctx.strokeRect(0, 0, canvas.width, canvas.height);
    }

    ctx.font = style.font
    ctx.fillStyle = style.color
    ctx.textBaseline = 'top'
    ctx.textAlign = 'center'

    let alignX = 0

    if (style.position === 'center') {
      alignX = canvas.width / 2;
    } else if (style.position === 'left') {
      ctx.textAlign = 'left'
      alignX = 40
    } else {
      ctx.textAlign = 'right'
      alignX = canvas.width - 40
    }

    let lineWidth = 0
    let lastSubStrIndex = 0
    let offsetY = 0
    for (let i = 0; i < content.length; i++) {
      lineWidth += ctx.measureText(content[i]).width;
      if (lineWidth > canvas.width - 120) {
        ctx.fillText(content.substring(lastSubStrIndex, i), alignX, offsetY);
        offsetY += fontHeight * style.lineHeight
        lineWidth = 0
        lastSubStrIndex = i
      }
      if (i === content.length - 1) {
        ctx.fillText(content.substring(lastSubStrIndex, i + 1), alignX, offsetY);
      }
    }
  }


  return {
    init
  }
})()




//ajax生成文章海报
$('body').on('click', '.cr_poster', function() {
	var post_id = $(this).attr('poster-data');
  $('.poster_box').remove();
	$('#share_modal_'+post_id+' .poster_box_ap').append('<div class="poster_box"></div>');

  function Posterdown(e){
    if (e == null) {
      return;
    }

    var modal = '#share_modal_'+post_id;

    var url = $(''+modal+' .poster_box img').attr('src');
    $(''+modal+' .post_share_box').removeClass('hide');
    $(''+modal+' .poster_download').attr('href',url).attr('download','poster_' + post_id + '.png');
    $
  }


	$.ajax({
		type: "POST",
		url:Theme.ajaxurl,
		dataType: 'json',
		data: {
			action: 'pix_create_poster',
      post_id: post_id,
			},	
		beforeSend: function () {
      cocoMessage.info('海报生成中..');
			$('#share_modal_'+post_id+' .poster_box').append('<div class="loading_box"><div uk-spinner></div></div>');
		},
		success: function(data){
      cocoMessage.success('海报已生成');
      $('.loading_box').remove();
      poster.init({
        banner: data.banner,
        selector: '.poster_box',
        title: data.title,
        content: data.content,
        logo: data.logo,
        qrcode: data.qrcode,
        description: data.des,
        callback: Posterdown
      });
		}	
	});
});	
PIX优化分享生成海报样式(底部LOGO/描述/二维码)-似水流年

图片的上半部分没有什么问题,在于调整底部LOGO、描述和二维码的样式。

一、LOGO 的尺寸由 canvas.drawImage() 方法的参数直接定义,代码位置在 init 函数的 image.onload 回调中(绘制 LOGO 的核心逻辑),具体代码段如下:

        var logowidth = logo.width;
        var logoheight = logo.height;
        var scale = logowidth / logoheight;
        var logoh = 60;
        var cwidth = logoh * scale;
        ctx.drawImage(logo, 60, $canvas.height / 1.2 + 30, cwidth, logoh);

        ctx.drawImage(qrcode, $canvas.width - 160, $canvas.height / 1.2 + 20, 120, 120);

修改方式:
调整 LOGO 高度:直接修改 var logoh = 60; 中的 60(单位为 px),例如改为 80 会让 LOGO 整体变大,改为 40 会变小。
调整 LOGO 宽度:无需手动改 cwidth,因为它是通过 logoh * scale 自动计算的(保证 LOGO 不拉伸变形);若想强制改宽度,可直接替换 cwidth 为固定值(如 100),但可能导致 LOGO 变形。

调整二维码高度和宽度:最后一行括号内参数依次为「图片对象、x轴位置、y轴位置、宽度、高度」,其中最后两个参数 120, 120 就是控制二维码大小的关键,改为 150, 150 会让二维码变大,改为 100, 100 会让二维码变小。

二、控制 LOGO 与描述(description)的上下左右位置、二维码上下左右位置。

上段代码中:

ctx.drawImage(logo, 60, $canvas.height / 1.2 + 30, cwidth, logoh);

ctx.drawImage(logo, x, y, 宽, 高) 中的第2个参数60是LOGO的X坐标(数值越大,LOGO 越靠右),第3个参数height / 1.2 + 30是LOGO的Y轴坐标。$canvas.height 是海报画布的总高度(默认 1160px,在文件代码开头位置设置),$canvas.height / 1.2 是固定基准位置,+30 是基准位置的偏移量(数值越大,LOGO 越靠下)。

上段代码中:

ctx.drawImage(qrcode, $canvas.width - 160, $canvas.height / 1.2 + 20, 120, 120);

$canvas.width - 160是X轴坐标,$canvas.height / 1.2 + 20是Y轴坐标,+20 是偏移量(数值越大,二维码越靠下)。

本段代码中:

        //标题文字
        ctx.drawImage($title, 20, $canvas.height / 2 + 20)
        ctx.drawImage($content, 20, $canvas.height / 2 + 120)
        ctx.drawImage($description, 20, $canvas.height / 1.2 + 110)
        ctx.strokeStyle = 'rgba(122, 122, 122, 0.5)';

ctx.drawImage($description, x, y)中的第2个参数20是描述文本的X坐标(数值越大,描述文本越靠右),第3个参数$canvas.height / 1.2 + 110是描述文本的Y轴坐标。同理,$canvas.height / 1.2 是基准位置,+110 是偏移量(数值越大,描述文本越靠下)。

以本站为例,最终修改代码如下:

        //logo和二维码
        var logowidth = logo.width;
        var logoheight = logo.height;
        var scale = logowidth / logoheight;
        var logoh = 200;
        var cwidth = logoh * scale;
        ctx.drawImage(logo, 70, $canvas.height / 1.2 - 20, cwidth, logoh);

        ctx.drawImage(qrcode, $canvas.width - 280, $canvas.height / 1.2 - 20, 220, 220);

        //标题文字
        ctx.drawImage($title, 20, $canvas.height / 2 + 20)
        ctx.drawImage($content, 20, $canvas.height / 2 + 120)
        ctx.drawImage($description, 20, $canvas.height / 1.2 + 170)
        ctx.strokeStyle = 'rgba(122, 122, 122, 0.5)';

最终效果如下:

PIX优化分享生成海报样式(底部LOGO/描述/二维码)-似水流年
Comments | 6 条评论
  • ymz316

    Firefox 142 Firefox 142 Windows 10 Windows 10 cn中国 中国电信 ip address 240e:381:dad8:b400:*:*

    随着博主技术的进步,博客功能是越来越丰富了😀

    • 似水流年

      IBrowse r IBrowse r Android 12 Android 12 cn中国–河南–漯河 电信 ip address 222.89.*.*

      看见值得学习的就想抄一下。🤭

  • 彬红茶

    Google Chrome 116 Google Chrome 116 Windows 10 Windows 10 cn中国 中国电信 ip address 240e:3ba:ca1:7ef1:*:*

    厉害!和子比真的非常像!

    • 似水流年

      IBrowse r IBrowse r Android 12 Android 12 cn中国–河南–漯河 电信 ip address 222.89.*.*

      主题作者说源代码就有一部分来自子比。

  • obaby

    Google Chrome 138 Google Chrome 138 Android 10 Android 10 cn中国 中国联通 ip address 2408:8418:e10:1979:*:*

    这个配图感觉不高级 有些乱
    是不是优化下配色?

    • 似水流年

      IBrowse r IBrowse r Android 12 Android 12 cn中国 中国联通 ip address 2408:8220:5f12:11b0:*:*

      回头我让AI优化一下。

消息盒子
# 您需要首次评论以获取消息 #
# 您需要首次评论以获取消息 #

只显示最新10条未读和已读信息