Создайте море звезд с кодом

JavaScript визуализация данных Canvas
Создайте море звезд с кодом

Об авторе Wuyue Ant Financial Data Experience Техническая команда

Наше путешествие - это море звезд
- Манифест Али

Как построить море Али из звезд с кодом?
Во-первых, мы представляем сценарий, происходящий в настоящее время, звездное небо, метеор по небу время от времени, вращение Земли, удобные услуги для миллионов людей в разных местах, потому что Али предоставил в сделке.

Эффект следующий:

embed: sky.mov

Структурная декомпозиция

Как построить приведенную выше сцену, разложить, чтобы получить следующую структуру:

звездное небо

В первую очередь должно быть небо, прямоугольник и черный фон.

Мы предварительно устанавливаем ширину, высоту и высоту, а трехмерное пространство должно иметь глубину, предварительно установленную как глубина.
В соответствии с куском мерцающих звезд, во-первых, в пространстве должна быть сгенерирована связка звезд, а во-вторых, звезды должны продолжать мерцать.Предварительно изменяется в соответствии с линейной разностью.Темная) часть кода ключа составляет:

var starts = [];
for (let i = 0; i < 10000; i++) {
  starts.push({
    x: Math.random() * width,
    y: Math.random() * height,
    z: Math.random() * depth,
    //当前进度 0 - 1, 当前大小按  (1 - t) * minSize + t * maxSize
    t: Math.random(),
    //变化方向
    direction: Math.random(), 
    //变化步长 暂定为常量 后面可以根据需要每个星星都不一样
    step: 0.01,
    //最小
    minSize: 10,
    //最大
    maxSize: 20 
  });
}
animate();
function animate () {
  requestAnimationFrame(animate);
  starts.forEach((item) => {
    let { t, minSize, maxSize } = item;
    //计算当前尺寸
    item.size = (1 - t) * minSize + t * maxSize;
    //改变进度
    if (t > 1) {
      //如果已经最大了 则开始减少
      item.direction = -1; 
    } else if (t < 0) {
      //最小了 则开始变大
      item.direction = 1; 
    }
    //修改进度
    item.t += item.step * item.direction;
  });
  //根据上面的值进行渲染
  render();
}

На данный момент мы завершили звезды. Но звезды мерцают на небе, и должны быть падающие звезды, чтобы было достаточно красиво. Метеор — это звезда, которая быстро пересекает линию на небе. Итак, это может быть следующим образом:

//流星处理逻辑
var falls = [];
for (let i = 0; i < 10; i++) {
  falls.push({
    //流星的起点
    start: {
      x: Math.random() * width,
      y: Math.random() * height,
      z: Math.random() * depth
    },
    //流星的终点
    end: {
      x: Math.random() * width,
      y: Math.random() * height,
      z: Math.random() * depth
    },
    //当前进度 0 - 1, 当前位置按 二次贝塞尔曲线计算即可 
    t: Math.random(),
    step: 0.01
  });
}

function animate () {
  requestAnimationFrame(animate);
  starts.forEach((item) => {
    let { t, start as p0, end as p2} = item;
    //随意设置一个控制点
    let p1 = {
      x: p0.x,
      y: (p0.y + p2.y) / 2,
      z: p2.z 
    };
    //按二次贝塞尔曲线 计算当前位置
    //(1 - t) * (1 - t) * p0 + 2 * t * (1 - t ) * p1  + t * t * p2
    
    let tmp = {
      x: (1 - t) * (1 - t) * p0.x + 2 * t * (1 - t ) * p1.x + t * t * p2.x,
      y: (1 - t) * (1 - t) * p0.y + 2 * t * (1 - t ) * p1.y + t * t * p2.y,
      z: (1 - t) * (1 - t) * p0.z + 2 * t * (1 - t ) * p1.z + t * t * p2.z
    };
    item.t += item.step;
    item.tmp = tmp;
    //改变进度
    if (t > 1) {
      //如果已经最大了变为0 
      item.t = 0; 
      //重置起始点
      item.start = {
        x: Math.random() * width,
        y: Math.random() * height,
        z: Math.random() * depth
      };
      item.end = {
        x: Math.random() * width,
        y: Math.random() * height,
        z: Math.random() * depth
      };
    }
  });
  //根据上面的值进行渲染
  render();
}

Конечно, вышесказанное только для удобства объяснения.闪烁а также流星Эффект разделен.Фактически две части имеют перекрывающуюся логику.Добавьте флаг к состоянию звезды, будь то мерцание или метеор, и тогда две логики могут быть объединены.Подробности см. в части реализации кода на github. многословный.

земной шар

Со звездным небом мы собираемся построить землю. Земля примерно разделена на две части, сначала создайте шар в начале трехмерного пространства, а затем покройте землю текстурированной кожей, которая представляет собой плоскую карту от -90 ~ 90, -180 -180. плоская карта выглядит следующим образом:

Затем ключевым моментом является то, что торговые точки на земле должны быть выделены, чтобы дать некоторые эффекты выделения на соответствующих широте и долготе. Конкретный код позиционирования выглядит следующим образом:

  //经纬度转x,y平面坐标
  lnglatToXY ({lng, lat}, width, height) {
    let x = (lng - (-180)) / 360 * width;
    let y = Math.abs((lat - 90) / 180) * height;
    return {
      x,y
    };
  }

Затем нужно произвести текстуры, большие фоновые картинки и нарисовать картинки по положению на горячем холсте.

    //渲染纹理
    async function render () {
      //全球背景图片
      let worldBg = await util.loadImg(worldSrc);
      //热点背景图片
      let hotBg = await util.loadImg(hotSrc);
      //画大背景
      ctx.drawImage(worldBg, 0, 0, width, height);
      //按经纬度绘制热点
      data.forEach((item) => {
        let lng = item.lnglat[0];
        let lat = item.lnglat[1];
        let {x, y} = util.lnglatToXY({lng, lat}, width, height);
        ctx.drawImage(hotBg, x - size / 2, y - size / 2, size, size);
      });
      //生产纹理,然后 直接映射蒙到球上就可以了
      let texture = new THREE.Texture(canvas);
      texture.needsUpdate = true;
      return texture;
    }

хорошо, земля формируется здесь. Затем позвольте земле вращаться вокруг оси Y каждый кадр.

animate();
function animate () {
  requestAnimationFrame(animate);
  earth.rotation.y += 0.01;
  render();
}

торговая линия

ok Вращающаяся земля также доступна, и ниже следует нарисовать линию транзакции на земле. Или сначала должно быть преобразование широты и долготы в пространственные координаты. Конкретный принцип заключается в следующем.

  //经纬度转空间坐标的具体代码, r 为球体半径
  lnglatToXYZ ({lng, lat}, r) {
    var phi = (90 - lat) * Math.PI / 180;
    var theta = -1 * lng * Math.PI / 180;
    return {
      x: r * Math.sin(phi) * Math.cos(theta),
      y: r * Math.cos(phi),
      z: r * Math.sin(phi) * Math.sin(theta)
    };
  }

Итак, совершенно ясно, что линия, перпендикулярная земле, имеет одну и ту же долготу и широту, но разные радиусы. Следовательно, p1 и p2 можно рассчитать по приведенной выше формуле для определения прямой линии. Затем добавьте контрольные точки и переменные направления, такие как мерцающее звездное небо, и большое количество отрезков линий будет колебаться. Код ключа следующий:

//原始数据
let lines = [
  {lng, lat },
  {lng, lat },
  ....
];

//生成一些控制参数
lines.forEach((item) => {
  //控制点 0 - 1 用来计算直线的终点 t * length 这里随机数是为了 不同的线初始状态不同
  item.t = Math.random(); 
  //变化方向
  item.direction = Math.random() > 0.5 ? 1 : -1;
  //变化速度 暂定都一样
  item.step = 0.01;
  //线段的长度 这里可以根据实际数据 比如 value来映射长度 本出为了示意 用随机数了
  item.length =  Math.random() * r;
});

animate();
function animate () {
  requestAnimationFrame(animate);
  lines.forEach((item) => {
    let p1 = lnglatToXYZ(item.lng, item.lat, r);
    //当前的终点 用 t 
    let p2 = lnglatToXYZ(item.lng, item.lat, r + item.length * t);
    //根据 p1, p2 即可绘制一条直线
    if (item.t > 1) {
      item.direction = -1; 
    } else if (item.t < 0) {
      item.direction = 1; 
    }
    item.t += item.direction * item.step;
  });
  render();
}

ok Вышеупомянутые мерцающие звезды, метеоры, проносящиеся по горизонту, вращающаяся земля и дышащие торговые линии, объединенные в соответствии с соответствующими позициями, это море звезд. Конечно, увидев это, вы можете спросить, звезды есть, а как же море, ведь если присмотреться, то поверхность земли полна воды, то есть моря.

Наконец, исходный код прилагается для справки. https://github.com/liuwuyue/земля Конкретный эффект можно найти здесь http://yiqihaiqilai.com, http://yiqihaiqilai.com?line (персональный сервер, может быть исчерпан, если вы его не видите, пожалуйста, скачайте исходный код и запустите его, не Не используйте его, чтобы найти меня)

Последнее, что важно, в нашей группе еще много таких сценариев. Добро пожаловать, присоединяйтесь. Заинтересованные студенты могут следить за колонкой или отправлять свои резюме на 'wuyue.lwy####alibaba-inc.com'.replace('# # ##', '@'), давайте вместе нарисуем Али море звезд. ~

Оригинальный адрес:GitHub.com/proto team/no…