弹幕——无穷的远方,无数的人们,都和我有关

264 阅读4分钟

前言

随着各类视频平台的不断发展,影视剧资源占比向视频平台倾斜。除了紧凑的剧情等客观因素的存在,如何调动观众的心绪也是重要的一点,于是弹幕应运而生了。早期的弹幕只是一段可见的文字,现在则别具一格,剧情的起承转合由观众们共同鉴赏和讨论,这不仅营造了独特的观剧氛围引起共鸣,而且有助于观众自主推广,还能实时反馈观众的看法,一举多得。借助MarsCode也可以实现一些常见的弹幕样式。

比如:

横向.gif

弹幕基础:输入框、发送按钮、画布

首先我们需要使用MarsCode构建一个简单的弹幕模式,在对话框中输入以下内容:

  1. 用JS构建一个包含输入框和发送按钮的弹幕

  2. 完善loop函数

  3. 完善屏幕、文本框和按钮模式看上去更美观

image.png

const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        const danmakuInput = document.getElementById('danmaku-input');
        const sendBtn = document.getElementById('send-btn');
        let danmakus = []; // 存储所有弹幕的数组
        
        sendBtn.addEventListener('click', function() {
            const text = danmakuInput.value;
            if (text!== '') {
                sendDanmaku(text);
                danmakuInput.value = '';
            }
        });
        // 横着方向
        function sendDanmaku(text) {
            // 将弹幕内容添加到数组中
            danmakus.push({
                text: text,
                x: canvas.width,
                y: Math.random() * canvas.height,
                speed: 2,// 弹幕的速度
            });
        }

        function loop() {
            // 清除画布
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            // 遍历所有弹幕,绘制并更新它们的位置
            danmakus.forEach(function(danmaku) {
                //  字体设置
                
                ctx.fillStyle = 'white';
                ctx.font = '30px  LiSu';
                 for (let i = 0; i < danmaku.text.length; i++) {
                    ctx.fillText(danmaku.text[i], danmaku.x+ i * 30, danmaku.y ); // 每个字符的 x坐标递增
                }
                // 更新弹幕的位置
                danmaku.x -= danmaku.speed;
            });

            // 循环调用 loop 函数
            requestAnimationFrame(loop);
        }

        // 启动 loop 函数
        window.onload = loop;

得到一个基础的弹幕样式,如下:

普通弹幕.gif

屏幕内容太单调,需要添加些新内容

需要弹幕文字和图片搭配显得生动些,我所设想的是文字和图片由下至上一起移动。

image.png

image.png

此时MarsCode在数组对象中添加了新的Image对象,我们可以根据与弹幕文字的搭配程度等选择相应图片。

// 将弹幕内容添加到数组中
danmakus.push({
 text: text,
 x: Math.random() * canvas.width, // 随机的 x 坐标
 y: canvas.height, // 初始 y 坐标为画布底部
 speed: 2, // 弹幕的速度
 image: new Image() // 创建一个新的 Image 对象
});
// 设置图片的 src 属性
danmakus[danmakus.length - 1].image.src = 'path/to/your/image.png'; // 替换为你的图片路径

并在loop函数中添加了图片的绘制,同时将文字绘制调整至图片绘制之后,否则图片会遮住文字。以及调转文字方向,从横向变纵向。

// 绘制图片
   if (danmaku.image.complete) {
     ctx.drawImage(danmaku.image, danmaku.x, danmaku.y + danmaku.text.length * 30); // 在文字下方绘制图片
   }
   // 绘制竖直方向的文字
   for (let i = 0; i < danmaku.text.length; i++) {
     ctx.fillText(danmaku.text[i], imageX, imageY + i * 30); // 每个字符的 y 坐标递增
   }

现在我们来调整弹幕文字和图片的位置,让文字在图片的上下左右都居中。

image.png

此时MarsCode调整了loop函数:在绘制文字之前,计算了文字的宽度和高度,并根据图片的宽度和高度来调整文字的位置,使其在图片中居中。

                // 计算文字的宽度和高度
                const textWidth = ctx.measureText(danmaku.text).width;
                const textHeight = 30 * danmaku.text.length; // 假设每个字符高度为30px

                // 计算文字在图片中的居中位置
                const imageX = danmaku.x + (danmaku.image.width - textWidth) / 2;
                const imageY = danmaku.y + (danmaku.image.height - textHeight) / 2;

当时疑惑文字宽度使用measureText,为什么高度不用measureText
原来measureText没有height的属性。原因是文本的高度在Canvas的2d上下文中相对比较复杂。

文本的高度通常由字体的设计和字号等因素决定,但不像宽度那样容易有一个固定的、与布局无关的测量方式。例如,不同的字体可能有不同的行间距、上下部留白等情况。

文字的绘制与图片的绘制在同一起点,也就是说左上角是重叠的,按理说上面的代码是可以让文字居中的,但是由于我之间调整了文字的方向,所以现在文字的宽度和高度应该交换一下。 其次文字之间有留白竖直方向文字的长度应该更长。调整之后达到预期,即:

const textWidth = 30 ;// 假设每个字符高度为30px,这个数值是根据设置的font大小来的
const textHeight =ctx.measureText(danmaku.text).width-12*danmaku.text.length ; 
// 绘制竖直方向的文字坐标用imageX、imageY
for (let i = 0; i < danmaku.text.length; i++) {
 ctx.fillText(danmaku.text[i], imageX, imageY + i * 30); // 每个字符的 y 坐标递增
}

至此,弹幕就全部完成了,如有需要可以自行调整角度和方向等。

弹幕1.gif

关于使用MarsCode的体验

  • 在不规定固定语言的情况下,MarsCode会优先选择最容易实现目的的语言。
  • 比较适合逐句的交流,类似与他人沟通的短句交流,不适合长篇大论式交流。
  • 没有开启新的对话时,会自动按照我之前问问题的逻辑,以及观察我的代码内容综合回答。

最后预祝MarsCode蒸蒸日上,更加智能,提供更好的服务!!