БлогNot. Тепловая карта и ландшафт на JavaScript

Тепловая карта и ландшафт на JavaScript

Кто в детстве не любил делать вот такие кляксы из фломастеров или красок (поводите не спеша и маленькими кругами мышкой над канвой):


очистить

А теперь это по-модному называется "тепловые карты" и много где используется. Наверное, легче всего приспособить для heat map имеющийся код, ссылка на оригинал которого есть в листинге.

Так как здесь отслеживается только перемещение мыши, на сенсорном экране для пальцев будет не очень удобно, но можно добавить обработку mousedown / touchstart как в этой заметке. Ниже показан исходник скрипта в виде файла .html в кодировке Юникода UTF-8 без стандартных обрамляющих тегов.

<div style="text-align: center;">
 <canvas id="heatCanvas" width="800" height="400"
  style="border: 1px dotted #333; background-color: white;"></canvas>
 <br>
 <span id="clearCanvas" style="cursor: pointer;">очистить</span>
</div>

<script>
// Объект simpleheat: https://github.com/mourner/simpleheat
(function () { 'use strict';

 function simpleheat (canvas) {
  if (!(this instanceof simpleheat)) { return new simpleheat(canvas); }
  this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas;
  this._ctx = canvas.getContext('2d');
  this._width = canvas.width;
  this._height = canvas.height;
  this._max = 1;
  this._data = [];
 }

 simpleheat.prototype = {
  defaultRadius: 25,
  defaultGradient: {
   0.2: 'magenta',
   0.4: 'blue',
   0.6: 'cyan',
   0.7: 'lime',
   0.8: 'yellow',
   0.9: 'orange',
   1.0: 'red'
  },
  data: function (data) {
   this._data = data; return this;
  },
  max: function (max) {
   this._max = max; return this;
  },
  add: function (point) {
   this._data.push(point); return this;
  },
  clear: function () {
   this._data = []; return this;
  },
  radius: function (r, blur) {
   blur = (blur === undefined ? 15 : blur);
   let circle = this._circle = document.createElement ('canvas'),
       ctx = circle.getContext('2d'),
       r2 = this._r = r + blur;
   circle.width = circle.height = r2 * 2;
   ctx.shadowOffsetX = ctx.shadowOffsetY = 200;
   ctx.shadowBlur = blur;
   ctx.shadowColor = 'black';
   ctx.beginPath();
   ctx.arc(r2 - 200, r2 - 200, r, 0, Math.PI * 2, true);
   ctx.closePath();
   ctx.fill();
   return this;
  },
  gradient: function (grad) {
   let canvas = document.createElement('canvas'),
       ctx = canvas.getContext('2d'),
       gradient = ctx.createLinearGradient(0, 0, 0, 256);
   canvas.width = 1;
   canvas.height = 256;
   for (var i in grad) gradient.addColorStop(i, grad[i]);
   ctx.fillStyle = gradient;
   ctx.fillRect(0, 0, 1, 256);
   this._grad = ctx.getImageData(0, 0, 1, 256).data;
   return this;
  },
  draw: function (minOpacity) {
   if (!this._circle) this.radius(this.defaultRadius);
   if (!this._grad) this.gradient(this.defaultGradient);
   let ctx = this._ctx;
   ctx.clearRect(0, 0, this._width, this._height);
   for (let i = 0, len = this._data.length, p; i < len; i++) {
    p = this._data[i];
    ctx.globalAlpha = Math.max(p[2] / this._max, minOpacity === undefined ? 0.05 : minOpacity);
    ctx.drawImage(this._circle, p[0] - this._r, p[1] - this._r);
   }
   let colored = ctx.getImageData(0, 0, this._width, this._height);
   this._colorize(colored.data, this._grad);
   ctx.putImageData(colored, 0, 0);
   return this;
  },
  _colorize: function (pixels, gradient) {
   for (let i = 3, len = pixels.length, j; i < len; i += 4) {
    j = pixels[i] * 4;
    if (j) {
     pixels[i - 3] = gradient[j];
     pixels[i - 2] = gradient[j + 1];
     pixels[i - 1] = gradient[j + 2];
    }
   }
  }
 };
 window.simpleheat = simpleheat;
})();
// Конец кода simpleheat


window.addEventListener ('load', function (e) {

 window.requestAnimationFrame = 
  window.requestAnimationFrame || 
  window.mozRequestAnimationFrame ||
  window.webkitRequestAnimationFrame || 
  window.msRequestAnimationFrame;

 let data = []; //начальные данные
 let heat = simpleheat('heatCanvas').data(data).max(20), frame; //создание карты

 function draw() { heat.draw(); frame = null; } //отрисовка
 draw();

 document.getElementById('heatCanvas').onmousemove = function (e) {
  heat.add([e.offsetX, e.offsetY, 1]);
  frame = frame || window.requestAnimationFrame (draw);
 }

 document.getElementById('clearCanvas').onclick = function (e) {
  heat.clear(); draw();
 }

});
</script>
<noscript>
 <div style="text-align: center;">
  Нужен включённый в браузере JavaScript для работы приложения
 </div>
</noscript>

Что до ландшафтов, вот несколько ссылок, от простых как в Dwarf Fortress (ещё одна культовая программистская игра) до довольно мощных:

прямоугольники, 4 вида ландшафта

на 6-угольниках

Очень навороченный генератор сказочных карт

Ещё один генератор островов с параметрами

Кстати, о дварфах:

Dr. Evil warns: Snow White and the Seven Dwarfs is a racist tale. Soot Black and the Seven Dwarfs matters!

(C)

03.07.2020, 23:29 [1436 просмотров]


теги: javascript цвет графика

К этой статье пока нет комментариев, Ваш будет первым