个人项目平台的构建之旅:从过程与探索中成长的作品集

本文详细记录了设计师Mike van der Sanden如何将个人作品集升级为互动式项目平台的全过程。文章深入探讨了技术选型、交互设计实现,包括使用Nuxt.js框架构建前端、Prismic CMS管理内容,以及通过p5.js和Three.js实现创意视觉效果的具体代码实现。

路径交汇:从灵感板到代码实验

今年夏天,我创建了个人项目平台。这个想法并非一开始就规划好的,而是在创作过程中逐渐成型。

路径一:必要的灵感空间

作为设计师,并非每天都能充满灵感。为了应对创意枯竭,我开始构建一个作为"快乐空间"的情绪板。每当遇到让我会心一笑的参考内容,我就将其添加进去 - 包括梦想办公室的构想、引起共鸣的语录,以及各种图像片段。

路径二:Instagram实验

2022年12月的一个晚上,我与设计师朋友小酌时开始随意创作。由于工作中更多承担管理角色,我十分怀念设计过程。于是创建了Instagram账号,发布了第一个Processing草图。

随着创作增多,草图变得更具互动性,但让我困扰的是它们只能在本地运行。我开始分享快速教程,并惊讶地收到大量积极反馈。

平台构建技术实践

技术选型

我具有PHP和JavaScript背景(ES6之前的传统技术)。为了学习新技术,经过调研后选择了Nuxt.js - 相比Next.js更易设置。配合几年前使用过的Prismic CMS,构成了平台的技术基础。

英雄区域交互实现

我希望访客能立即感受到我的工作风格。通过鼠标在画布上绘制对象,这些对象与自然生长相关联。最初花朵按比例从小到大生长,但后来决定采用随机缩放方式,以反映工作中控制与混乱之间的张力。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public getPortion(): p5.Image {
  const original = this.getNext();
  if (!original) return null;

  const ow = original.width;
  const oh = original.height;
  const sx = Math.random() * ow;
  const sy = Math.random() * oh;

  let sw = Math.round((ow - sx) * Math.random()) + 10;
  let sh = Math.round((oh - sy) * Math.random()) + 10;

  const copy = this.p.createImage(sw, sh);
  copy.copy(original, sx, sy, sw, sh, 0, 0, sw, sh);
  return copy;
}

页脚交互设计

为平衡英雄区域,页脚也设计了交互效果。基于旧草图添加深度和纹理,创造抽象海洋的感觉。通过垂直移动和鼠标X轴移动时的色调变化,带来平静和专注感。

瀑布流网格实现

我决定使用CSS Grid和JavaScript构建瀑布流布局,根据CMS中设置的宽高比计算图像应跨越的行数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
function applyMasonry() {
  const grid = document.querySelector('.masonry-grid');
  const items = grid?.querySelectorAll('.masonry-item');
  
  if (!grid || !items) return;

  const rowHeight = parseInt(getComputedStyle(grid).getPropertyValue('grid-auto-rows'));
  const gap = parseInt(getComputedStyle(grid).getPropertyValue('gap') || 0);

  items.forEach(item => {
    const media = item.querySelector('.masonry-item__image-container');
    const info = item.querySelector('.masonry-item__info-container');
    
    if (!media || !info) return;

    const itemHeight = media.getBoundingClientRect().height + info.getBoundingClientRect().height;
    const rowSpan = Math.ceil((itemHeight + gap) / (rowHeight + gap));
    
    item.style.gridRowEnd = `span ${rowSpan}`;
    item.style.opacity = 1;
  });
}

技术决策:p5.js与Three.js的对比

在为logo创建3D像素化场景时,最初使用p5.js但性能较差。转而使用Three.js后,即使添加更多细节也能保持流畅性能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let instanceId = 0;
for (let z = 0; z < detail; z++) {
  for (let y = 0; y < detail; y++) {
    const flippedY = detail - 1 - y;
    for (let x = 0; x < detail; x++) {
      const sampleX = Math.floor((x / detail) * imgDetail);
      const sampleY = Math.floor((flippedY / detail) * imgDetail);
      const sampleZ = Math.floor((z / detail) * imgDetail);

      const brightness1 = getBrightnessAt(imgData, imgDetail, sampleX, sampleY);
      const brightness2 = getBrightnessAt(imgData, imgDetail, sampleZ, sampleY);

      if (brightness1 < 100 && brightness2 < 100 && instanceId < maxInstances) {
        dummy.position.set(
          x * cellSize - (detail * cellSize) / 2,
          y * cellSize - (detail * cellSize) / 2,
          z * cellSize - (detail * cellSize) / 2
        );
        dummy.updateMatrix();
        mesh.setMatrixAt(instanceId, dummy.matrix);
        instanceId++;
      }
    }
  }
}

资源分享与持续改进

平台专门设置了资源与代码区域,分享超过20个项目的源代码和创作过程。这个平台永远不会"完成" - 这正是其核心价值:作为与编码工具互动的空间,分享草图供进一步探索,让创作过程始终保持可见。

如果你也是设计师或程序员,希望这个平台能激励你开始或继续自己的副业项目。这才是保持创造力活力的方式。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计