• 作者:老汪软件技巧
  • 发表时间:2024-09-28 15:01
  • 浏览量:

1949年9月25日晚,毛泽东同志召开了国旗、国徽、国歌、纪年、国都协商座谈会。在这次会上,毛泽东同志拿着五星红旗的图案说:这个图案表现我们革命人大团结。现在要大团结,将来也要大团结。因此,现在也好,将来也好,又是团结,又是革命.

1. 前言

又是一年国庆将至,我心激荡,如今到了经济下行期,但是只要咱团结一致,一定能够度过难关,毕竟再困难,这江山还是依旧如此磅礴。

看到掘金酱发布的豆包MarsCode放“码”过来的活动,本着让进步发生的初心,八青妹想和豆包MarsCode一起来制作一面飘扬的五星红旗,献礼2024国庆。

2. 步骤分析2.1 了解五星红旗

虽然每周一都能看到鲜艳的五星红旗,但是要画出一面五星红旗,还是得从五星红旗的图案细节入手。从百度百科查阅中华人民共和国国旗得到的描述如下(结合国旗墨线图会更加清晰):

国旗墨线图

(一)旗面为红色,长方形,其长与高为三与二之比,旗面左上方缀黄色五角星五颗。一星较大,其外接圆直径为旗高十分之三,居左;四星较小,其外接圆直径为旗高十分之一,环拱于大星之右。旗杆套为白色。

(二)五星之位置与画法如下:

甲、为便于确定五星之位置,先将旗面对分为四个相等的长方形,将左上方之长方形上下划为十等分,左右划为十五等分。

乙、大五角星的中心点,在该长方形上五下五、左五右十之处。其画法为:以此点为圆心,以三等分为半径作一圆。在此圆周上,定出五个等距离的点,其一点须位于圆之正上方。然后将此五点中各相隔的两点相联,使各成一直线。此五直线所构成之外轮廓线,即为所需之大五角星。五角星之一个角尖正向上方。

丙、四颗小五角星的中心点,第一点在该长方形上二下八、左十右五之处,第二点在上四下六、左十二右三之处,第三点在上七下三、左十二右三之处,第四点在上九下一、左十右五之处。其画法为:以以上四点为圆心,各以一等分为半径,分别作四个圆。在每个圆上各定出五个等距离的点,其中均须各有一点位于大五角星中心点与以上四个圆心的各联结线上。然后用构成大五角星的同样方法,构成小五角星。此四颗小五角星均各有一个角尖正对大五角星的中心点。

2.2 如何让旗帜飘动

在古柳大佬的文章中讲到飘动的旗帜。引用下该文章的部分内容:

古佬是将一面旗帜的图片作为平面的贴图。使用shader将该平面上顶点坐标沿着x轴和y轴做曲线移动实现旗帜的飘扬效果。

八青妹试图在这个基础上进行改动下。八青妹这篇文章-的中心思想就是,将canvas贴图作为平面几何体材质的texture2D贴图进行展示。

如此,便有了技术思路。

2.3 技术思路

使用豆包MarsCode作为辅助编程工具,将步骤进行拆解,

八青妹尝试了下MarsCode IDE ,感兴趣的可以试试。

3. 实现过程3.1 绘制红色旗帜

八青妹:“ 在尺寸为300 * 200的canvas画布上,绘制一面底色为红色的旗帜,尺寸为300 * 200”。

豆包MarsCode:

直接使用该代码,然后点击右侧栏的第四个形状像眼睛的按钮【Webview】,可以直接预览结果。

效果可以的,鼓掌,还措手不及的给画布描了黑边。如果觉得这样切换预览有点麻烦,可以点击按钮【Open In Browser】在另一个页面打开。

3.2 绘制大五角星

八青妹:“在上述的红色旗帜上,以中心坐标为(50,60),绘制半径为30的大五角星”。

豆包MarsCode回复的部分截图:

预览的结果如下:

这个可不是八青妹想要的效果啊!但是根据豆包MarsCode的思路修改下,首先要肯定的是思路是对的,但是在连接每个点的时候,需要改进下,这里只连接了五个点,考虑得过于简单了。尝试跟豆包再说的明确一些

八青妹:“修正五角星的画法为:以此(50,60)点为圆心,以30为半径作一圆。在此圆周上,定出五个等距离的点,其一点须位于圆之正上方。然后将此五点中各相隔的两点相联,使各成一直线”。

豆包MarsCode回复的关键代码:

        ctx.translate(x, y);
        ctx.rotate(3 * Math.PI / 5);
        ctx.moveTo(0, -radius);
        for (var i = 1; i <= 5; i++) {
          ctx.lineTo(
            radius * Math.sin((3 - i) * 2 * Math.PI / 5),
            -radius * Math.cos((3 - i) * 2 * Math.PI / 5)
          );
        }
        ctx.closePath();
        ctx.fillStyle = 'yellow';
        ctx.fill();

结果是:

emmmm,还是先来理解下五角星的绘制步骤吧。八青妹草草的绘制了下,将就看着:

在圆周上,定出五个等距离的点(蓝色的点)按照绿色文字标识的数字顺序,连接五个点。

八青妹:“需要进一步改进五角星的绘制,先定义pointArray放置该圆周上的五个顶点,按照该顺序进行连接:第一个点连第三个,再连第五个,再连第二个,再连第四个,第四个闭合第一个”

应用豆包MarsCode提供的代码,查看预览结果,如下所示:

很明显,五个点的角度计算错了,错误在于这行代码:

 const angle = Math.PI / 5 ; // 角度间隔

修改下:

 const angle = Math.PI / 5 *2; // 角度间隔

预览效果:

鼓掌,五角星绘制成功!但是角度需要调整下,需要一个顶角朝着最上方。

八青妹:“将上述的五角星其中一个顶点朝着正上方。”

豆包MarsCode回复的内容如下:

无脑替换了下,效果非常好。给豆包MarsCode点赞。

3.3 绘制小五角星

小五角星的绘制方法同大五角星一样,只是坐标、半径和旋转的角度不同。那么需要将绘制大五角星的方法进行函数封装下。

八青妹:“将绘制五角星的方法进行函数封装,入参为坐标、半径和旋转角度”。

豆包MarsCode:

很好,这里可以将const ctx去掉,因为前文在绘制红底的时候定义过了。

八青妹:“四个小五角星的坐标分别为(100,20)、(120,40)、(120,70)、(100,90),半径均为10,小五角星的旋转角度后有一个顶点朝着大五角星的中心点(50,50),用上述封装的方法绘制四个小五角星”。豆包MarsCode:

应用下该代码,可直接在鼠标位置,点击代码块的Insert 按钮。一键插入代码块。

然后看下效果:

太赞了,那复杂的三角函数运算不需要过脑子了,棒棒棒!

3.4 引入threejs

这个编辑器可以直接使用终端,跟一般编辑器的终端使用一样。

在终端输入

npm install three

先使用threejs绘制一个基础的页面。这次是新建了一个index.js页面,在页面内直接与豆包MarsCode对话。

八青妹:“引入threejs,使用THREE.PlaneGeometry(3, 2, 30, 30)和THREE.ShaderMaterial绘制一个红色的平面”

豆包MarsCode:

import * as THREE from 'three';
// 创建一个场景
const scene = new THREE.Scene();
// 创建一个相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 创建一个渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const geometry = new THREE.PlaneGeometry(3, 2, 30, 30);
const material = new THREE.ShaderMaterial({
    uniforms: {
        color: { value: new THREE.Color(0xff0000) }
    },
    vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
    fragmentShader: `
    uniform vec3 color;
    varying vec2 vUv;
    void main() {
      gl_FragColor = vec4(color, 1.0);
    }
  `
});
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
// 渲染场景
function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}
animate();

效果如下:

3.5 将canvas作为贴图进行覆盖

八青妹:“将id为canvas的canvas作为贴图,更换原来shadermaterial的入参色彩color”。

豆包MarsCode:

import * as THREE from 'three';
// 获取canvas元素
var canvas = document.getElementById("canvas");
// 获取绘图上下文
var ctx = canvas.getContext("2d");
// 绘制红旗背景
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 300, 200);
function drawStar(x, y, radius, rotation) {
  ctx.fillStyle = "#FFFF00";
  ctx.beginPath();
  const angle = Math.PI / 5 * 2;
  const pointArray = [];
  for (let i = 0; i < 5; i++) {
    pointArray.push({
      x: x + radius * Math.cos(angle * i + rotation),
      y: y - radius * Math.sin(angle * i + rotation)
    });
  }
  ctx.moveTo(pointArray[0].x, pointArray[0].y);
  ctx.lineTo(pointArray[2].x, pointArray[2].y);
  ctx.lineTo(pointArray[4].x, pointArray[4].y);
  ctx.lineTo(pointArray[1].x, pointArray[1].y);
  ctx.lineTo(pointArray[3].x, pointArray[3].y);
  ctx.closePath();
  ctx.fill();
}
// 使用封装后的函数绘制五角星
drawStar(50, 60, 30, Math.PI / 2);
// 使用封装后的函数绘制五角星
drawStar(100, 20, 10, Math.atan2(20 - 50, 100 - 50));
drawStar(120, 40, 10, Math.atan2(40 - 50, 120 - 50));
drawStar(120, 70, 10, Math.atan2(70 - 50, 120 - 50));
drawStar(100, 90, 10, Math.atan2(90 - 50, 100 - 50));
// 创建一个场景
const scene = new THREE.Scene();
// 创建一个相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 创建一个渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建一个纹理
const texture = new THREE.CanvasTexture(canvas);
// 创建一个平面几何体
const geometry = new THREE.PlaneGeometry(3, 2, 30, 30);
// 创建一个着色器材质
const material = new THREE.ShaderMaterial({
  uniforms: {
    map: { value: texture }
  },
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    uniform sampler2D map;
    varying vec2 vUv;
    void main() {
      gl_FragColor = texture2D(map, vUv);
    }
  `
});
// 创建一个平面网格
const plane = new THREE.Mesh(geometry, material);
// 将平面添加到场景中
scene.add(plane);
// 渲染场景
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();

豆包MarsCode将之前绘制五星红旗的方法也带出来了,完整的过了一遍。效果请看:

还不错,比我预期的好很多。

3.6 让五星红旗动起来

八青妹:“**上述的materialShader定义一个uTime,值随帧更改,并使得平面的顶点坐标的y值做sin运算 **”

豆包MarsCode:

由于这段实在不知道怎么跟它解释要具体怎么做,所以,手动copy下古柳大佬实现这段步骤顶点着色器中的代码片段,并修改下自己想要的参数替换下:

void main() {
  vec4 modelPosition = modelMatrix * vec4(position, 1.0);
  modelPosition.y += 0.08 * sin(modelPosition.x * PI * 1.0 + uTime);
  modelPosition.y += 0.05 * sin(modelPosition.z * PI * 1.5 + uTime);
  gl_Position = projectionMatrix * viewMatrix * modelPosition;
}

3.7 收尾工作

更改canvas的样式,让它置于三维显示的后面,让飘动的红旗更好的展示。下面是完整的代码片段,可直接预览看到效果。

4. 总结

与豆包MarsCode的合作整体来说很愉快,替八青妹省了不少事。但是依旧是作为辅助工具来使用,事在人为,希望大家也能通过AI工具写出让自己满意的功能。