资讯专栏INFORMATION COLUMN

炫酷粒子表白 | 听说女神都想谈恋爱了!

venmos / 2495人阅读

摘要:最近听女神说想谈恋爱了,嘿嘿,一定不能放过这个机会,给她来个不一样的表白。我们只需要判断不为就可以得到需要的字体形状数据了。能不能再给力一点说好的粒子系统,现在只是简单的画了一点。还有什么好玩的上面是将粒子摆成文字。

最近听女神说想谈恋爱了,✧(≖ ◡ ≖) 嘿嘿,一定不能放过这个机会,给她来个不一样的表白。

那么咱们就一起来把这个粒子系统玩出花来吧

演示地址:
https://es2049.studio/work-sh...

如何将一系列的粒子组成一句表白呢?

实现原理其实很简单,Canvas 中有个 getImageData 的方法,可以得到一个矩形范围所有像素点数据。那么我们就试试来获取一个文字的形状吧。

第一步,用 measureText 的方法来计算出文字适当的尺寸和位置。

// 创建一个跟画布等比例的 canvas
const width = 100;
const height = ~~(width * this.height / this.width); // this.width , this.height 说整个画布的尺寸
const offscreenCanvas    =document.createElement("canvas");
const offscreenCanvasCtx    =offscreenCanvas.getContext("2d");
offscreenCanvas.setAttribute("width", width);
offscreenCanvas.setAttribute("height", height);

// 在这离屏 canvas 中将我们想要的文字 textAll 绘制出来后,再计算它合适的尺寸
offscreenCanvasCtx.fillStyle = "#000";
offscreenCanvasCtx.font = "bold 10px Arial";
constmeasure=offscreenCanvasCtx.measureText(textAll); // 测量文字,用来获取宽度
const size = 0.8;
// 宽高分别达到屏幕0.8时的size

const fSize=Math.min(height * size * 10 / lineHeight, width * size * 10 / measure.width);  // 10像素字体行高 lineHeight=7 magic
offscreenCanvasCtx.font = `bold ${fSize}px Arial`;

// 根据计算后的字体大小,在将文字摆放到适合的位置,文字的坐标起始位置在左下方
const measureResize    =offscreenCanvasCtx.measureText(textAll);
// 文字起始位置在左下方
let left = (width - measureResize.width) / 2;
const bottom=(height + fSize / 10 * lineHeight) / 2;
offscreenCanvasCtx.fillText(textAll, left, bottom);

咱们可以 appendChild 到 body 里看眼

同学们注意,我要开始变形了 [推眼镜] 。
getImageData 获取的像素数据是一个 Uint8ClampedArray (值是 0 - 255 的数组),4 个数一组分别对应一个像素点的 R G B A 值。我们只需要判断 i * 4 + 3 不为 0 就可以得到需要的字体形状数据了。

// texts 所有的单词分别获取 data ,上文的 textAll 是 texts 加一起
Object.values(texts).forEach(item => {
    offscreenCanvasCtx.clearRect(0, 0, width, height);
    offscreenCanvasCtx.fillText(item.text, left, bottom);
    left += offscreenCanvasCtx.measureText(item.text).width;
    const data = offscreenCanvasCtx.getImageData(0, 0, width, height);
    const points = [];
        // 判断第 i * 4 + 3 位是否为0,获得相对的 x,y 坐标(使用时需乘画布的实际长宽, y 坐标也需要取反向)
    for (let i = 0, max = data.width * data.height; i < max; i++) {
        if (data.data[i * 4 + 3]) {
            points.push({
                x: (i % data.width) / data.width,                
                y: (i / data.width) / data.height
           });
       }
   }
        // 保存到一个对象,用于后面的绘制
    geometry.push({
        color: item.hsla,        
        points
    });
})

制定场景,绘制图形

文字图形的获取方式以及搞定了,那么咱们就可以把内容整体输出了。咱们定义一个简单的脚本格式。

// hsla 格式方便以后做色彩变化的扩展
const color1 = {h:197,s:"100%",l:"50%",a:"80%"};
const color2 = {h:197,s:"100%",l:"50%",a:"80%"};
// lifeTime 祯数
const Actions = [
    {lifeTime:60,text:[{text:3,hsla:color1}]},   
    {lifeTime:60,text:[{text:2,hsla:color1}]},  
    {lifeTime:60,text:[{text:1,hsla:color1}], 
    {lifeTime:120,text:[     
        {text:"I",hsla:color1},
        {text:"❤️",hsla:color2},
        {text:"Y",hsla:color1},
        {text:"O",hsla:color1},  
        {text:"U",hsla:color1}
   ]},
];

根据预设的脚本解析出每个场景的图形,加一个 tick 判断是否到了 lifeTime 切换到下一个图形重新绘制图形。

function draw() {
    this.tick++;
    if (this.tick >= this.actions[this.actionIndex].lifeTime) {
        this.nextAction();
   }
    this.clear();    
    this.renderParticles(); // 绘制点    
    this.raf = requestAnimationFrame(this.draw);
}

    function nextAction() {
        ....//切换场景 balabala..
        this.setParticle(); // 随机将点设置到之前得到的 action.geometry.points 上
    }

这样咱们基本的功能已经完成了。

能不能再给力一点

说好的粒子系统,现在只是 context.arc 简单的画了一点。那咱们就来加个粒子系统吧。

class PARTICLE {
    // x,y,z 为当前的坐标,vx,vy,vz 则是3个方向的速度
    constructor(center) {
        this.center = center; 
        this.x = 0;
        this.y = 0;        
        this.z = 0;        
        this.vx = 0;        
        this.vy = 0;        
        this.vz = 0;
   }
    // 设置这些粒子需要运动到的终点(下一个位置)
    setAxis(axis) {
        this.nextX = axis.x;        
        this.nextY = axis.y;        
        this.nextZ = axis.z;        
        this.color = axis.color;
   }
    step() {
        // 弹力模型 距离目标越远速度越快
        this.vx += (this.nextX - this.x) * SPRING;
        this.vy += (this.nextY - this.y) * SPRING;
        this.vz += (this.nextZ - this.z) * SPRING;
            // 摩擦系数 让粒子可以趋向稳定
       this.vx *= FRICTION;
       this.vy *= FRICTION;
       this.vz *= FRICTION;
        
        this.x += this.vx;        
        this.y += this.vy;        
        this.z += this.vz;
   }
    getAxis2D() {
        this.step();
        // 3D 坐标下的 2D 偏移,暂且只考虑位置,不考虑大小变化
        const scale = FOCUS_POSITION / (FOCUS_POSITION + this.z);
        return {
            x: this.center.x + (this.x * scale),
            y: this.center.y - (this.y * scale),
       };
   }
}

大功告成!

既然是 3D 的粒子,其实这上面还有不是文章可做,同学们可以发挥想象力来点更酷炫的。

还有什么好玩的

上面是将粒子摆成文字。那咱们当然也可以直接写公式摆出个造型。

// Actions 中用 func 代替 texts
{
    lifeTime: 100,
    func: (radius) => {
        const i = Math.random() * 1200;
        let x = (i - 1200 / 2) / 300;
        let y = Math.sqrt(Math.abs(x)) - Math.sqrt(Math.cos(x)) * Math.cos(30 * x);
        return {
        x: x * radius / 2,
        y: y * radius / 2,
        z: ~~(Math.random() * 30),
        color: color3
       };
   }
}

再把刚才文字转换形状的方法用一下

{
    lifeTime: Infinity,
        func: (width, height) => {
            if(!points.length){
                const img = document.getElementById("tulip");
                    constoffscreenCanvas = document.createElement("canvas");
                    constoffscreenCanvasCtx = offscreenCanvas.getContext("2d");

                const imgWidth = 200;
                const imgHeight = 200;
                offscreenCanvas.setAttribute("width", imgWidth);
                offscreenCanvas.setAttribute("height", imgHeight);
                offscreenCanvasCtx.drawImage( img, 0, 0, imgWidth, imgHeight);
                let imgData=offscreenCanvasCtx.getImageData( 0, 0, imgWidth, imgHeight);
                for ( let i = 0, max = imgData.width * imgData.height; i < max; i++) {
                    if (imgData.data[i * 4 + 3]) {
                        points.push({
                            x: (i % imgData.width) / imgData.width,
                            y: (i / imgData.width) / imgData.height
                       });
                   }
               }
           }

            
    const p= points[~~(Math.random() * points.length)]
            const radius = Math.min(width * 0.8, height * 0.8);
            return {
                    x: p.x * radius - radius / 2,
                    y: (1 - p.y) * radius - radius / 2,
                    z: ~~(Math.random() * 30),
                    color: color3
           };
       }
}

完美

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99899.html

相关文章

  • 炫酷粒子表白,双十一脱单靠它

    摘要:双十一光棍节又要来临了,每年这个时候都是本人最苦闷的时刻。能不能再给力一点说好的粒子系统,现在只是简单的画了一点。 showImg(https://segmentfault.com/img/remote/1460000016908379?w=1100&h=564); ​ 双十一光棍节又要来临了,每年这个时候都是本人最苦闷的时刻。日渐消瘦的钱包,愈发干涸的双手,虽然变强了,头却变凉...

    lauren_liuling 评论0 收藏0
  • 从小数学就不及格的我,竟然用极坐标系表白我的女神!(附代码)

    摘要:在两点间的关系用夹角和距离很容易表示时,极坐标系便显得尤为有用而在平面直角坐标系中,这样的关系就只能使用三角函数来表示。对于很多类型的曲线,极坐标方程是最简单的表达形式,甚至对于某些曲线来说,只有极坐标方程能够表示。 欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由郭诗雅发表于云+社区专栏 在数学中,极坐标系(英语:Polar coordinate system)是...

    yagami 评论0 收藏0
  • 漫画:程序员的“情人”节应该这样度过

    摘要:今天是年月号,一年一度的情人节又来了,今天对于很多年轻人来说是个特别的日子。该程序员在联谊会上认识了某艺术系女生,同乡的双方互有好感。 今天是2018年8月17号,一年一度的情人节又来了,今天对于很多年轻人来说是个特别的日子。但是作为屌丝界的一枚程序员,往往给人的印象是宅,不爱说话,智商不好,也不讨好哄女孩子欢心。但是,为什么我身边的程序员各个都是高智商,高颜值的小码农呢?或许,他们用...

    Shisui 评论0 收藏0
  • 听说你想 520 表白

    摘要:源码很简单键盘按,然后输入表白网页生成器打开网站,填入要表白的内容生成好的网页百度传情其实百度很早就有个传情功能,直接百度输入表白即可。 又到520,一个狂虐单身狗的日子,看看知乎的热门话题:showImg(https://segmentfault.com/img/remote/1460000019264481); 这里给大家提供几个虐狗的新姿势。 69 表白从 69 开始,给对方发个...

    Mr_zhang 评论0 收藏0
  • 【Python纪念册】哪些浪漫至极的表白程序—“你做我的男孩,我做你的宇宙”

    摘要:完整的资料源码都打包等你来取哈免费滴直接视频效果展示如下纪念册哪些浪漫至极的表白程序截图展示如下界面漫天花瓣飞舞。  导语 大家好,我是木木子!​​ 今日的表白案例上线啦~有没有期待?安排安排!源码基地见免费源码哈! 贴心的木子君也给你们好多爱心花瓣、以及表白的小程序!在主页的左侧哦! 这款...

    aisuhua 评论0 收藏0

发表评论

0条评论

venmos

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<