Atlaslog
Atlaslog / Examples / deck.gl / View Transition
deck.gl · Runnable Example

View Transition

Runnable deck.gl example: View Transition. Open the live demo, read the complete source, and copy the file into your project. No build step or API key.

Open standalone → Chinese notes All deck.gl examples
/demos/deckgl-view-transition.htmlLIVE
single HTML file · CDN dependencies onlyinterface may contain Chinese labels

Source

demos/deckgl-view-transition.html
<!--
  示例:deck.gl 视图过渡动画
  要点:插值 viewState 的经纬度、缩放、俯仰与方位角,在几座城市之间平滑飞行
  依赖:deck.gl 9.0 · jsDelivr CDN
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="robots" content="noindex,follow">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>视图过渡动画</title>
<style>
  html,body{height:100%;margin:0;background:#0d1117}
  body{display:flex;flex-direction:column;overflow:hidden;
       font:13px/1.6 -apple-system,"Segoe UI","PingFang SC",sans-serif;color:#e6e8ec}
  .bar{position:relative;z-index:5;flex:0 0 auto;padding:8px 12px;background:#161b22;border-bottom:1px solid #252c36;
       display:flex;gap:8px;align-items:center;font-size:12px;color:#8b93a1;flex-wrap:wrap}
  .bar b{color:#58a6ff;font-weight:600}
  .bar button{font:inherit;font-size:12px;padding:3px 10px;border-radius:5px;cursor:pointer;
              border:1px solid #303845;background:#1d2531;color:#c8d2e0}
  .bar button:hover{background:#253040}
  .bar button.on{background:#2c4a7a;border-color:#4f8ad6;color:#dfe9ff}
  .bar .sep{width:1px;height:18px;background:#2a313c;margin:0 4px}
  .progress{position:relative;flex:0 0 140px;height:4px;background:#242c38;border-radius:2px;
            overflow:hidden}
  .progress i{position:absolute;left:0;top:0;bottom:0;width:0;background:#4f9ef8}
  #cv{flex:1 1 auto;min-height:0;width:100%}
</style>
</head>
<body>
<div class="bar">
  <span>目标 <b id="name">全国</b></span>
  <span class="sep"></span>
  <button data-idx="0" class="on">全国</button>
  <button data-idx="1">北京</button>
  <button data-idx="2">上海</button>
  <button data-idx="3">成都</button>
  <button data-idx="4">广州</button>
  <span class="progress"><i id="bar"></i></span>
  <span>飞行中暂停拖拽 · 结束后可自由操作</span>
</div>
<canvas id="cv"></canvas>
<script src="https://cdn.jsdelivr.net/npm/deck.gl@9.0.35/dist.min.js"></script>
<script>
(function () {
  'use strict';

  if (typeof deck === 'undefined') {
    document.body.innerHTML =
      '<div style="padding:24px;color:#ff8c74">deck.gl 加载失败,请检查能否访问 cdn.jsdelivr.net</div>';
    return;
  }

  var targets = [
    { name: '全国', longitude: 108.5, latitude: 32.8, zoom: 3.9, pitch: 20, bearing: 0 },
    { name: '北京', longitude: 116.39, latitude: 39.90, zoom: 9.8, pitch: 48, bearing: -12 },
    { name: '上海', longitude: 121.47, latitude: 31.23, zoom: 10.2, pitch: 55, bearing: -24 },
    { name: '成都', longitude: 104.06, latitude: 30.57, zoom: 10.0, pitch: 50, bearing: 22 },
    { name: '广州', longitude: 113.26, latitude: 23.12, zoom: 10.3, pitch: 52, bearing: 8 }
  ];

  var clusters = [
    { name: 'BEIJING', lng: 116.39, lat: 39.90, color: [255, 150, 90, 210] },
    { name: 'SHANGHAI', lng: 121.47, lat: 31.23, color: [90, 180, 255, 210] },
    { name: 'CHENGDU', lng: 104.06, lat: 30.57, color: [120, 220, 170, 210] },
    { name: 'GUANGZHOU', lng: 113.26, lat: 23.12, color: [200, 160, 255, 210] }
  ];

  var points = [];
  for (var c = 0; c < clusters.length; c++) {
    var cl = clusters[c];
    for (var i = 0; i < 5000; i++) {
      var r = Math.sqrt(Math.random()) * 0.55;
      var a = Math.random() * Math.PI * 2;
      points.push({
        lng: cl.lng + Math.cos(a) * r,
        lat: cl.lat + Math.sin(a) * r * 0.8,
        color: cl.color,
        v: Math.random()
      });
    }
  }

  var pointLayer = new deck.ScatterplotLayer({
    id: 'points',
    data: points,
    getPosition: function (d) { return [d.lng, d.lat]; },
    getRadius: 2.6,
    radiusUnits: 'pixels',
    radiusMinPixels: 1.4,
    radiusMaxPixels: 5,
    getFillColor: function (d) { return d.color; },
    pickable: false
  });

  var labelLayer = new deck.TextLayer({
    id: 'cluster-labels',
    data: clusters,
    getText: function (d) { return d.name; },
    getPosition: function (d) { return [d.lng, d.lat]; },
    getSize: 14,
    sizeUnits: 'pixels',
    getColor: [222, 234, 250, 255],
    getPixelOffset: [0, -20],
    getTextAnchor: 'middle',
    getAlignmentBaseline: 'center',
    characterSet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ',
    background: true,
    getBackgroundColor: [10, 18, 30, 205],
    backgroundPadding: [4, 2],
    outlineWidth: 0.08,
    outlineColor: [8, 14, 26, 210],
    billboard: true
  });

  var view = targets[0];

  var deckgl = new deck.Deck({
    canvas: 'cv',
    width: '100%',
    height: '100%',
    viewState: view,
    controller: true,
    onViewStateChange: function (e) {
      view = e.viewState;
      deckgl.setProps({ viewState: view });
    },
    layers: [pointLayer, labelLayer]
  });

  var flying = false;
  var progress = document.getElementById('bar');
  var nameLabel = document.getElementById('name');

  function easeInOut(t) {
    return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
  }

  function lerp(a, b, t) { return a + (b - a) * t; }

  function flyTo(idx, btn) {
    if (flying) return;
    var end = targets[idx];
    var start = {
      longitude: view.longitude,
      latitude: view.latitude,
      zoom: view.zoom,
      pitch: view.pitch || 0,
      bearing: view.bearing || 0
    };
    var t0 = performance.now();
    var span = 1500;
    flying = true;

    nameLabel.textContent = end.name;
    deckgl.setProps({ controller: false });

    var buttons = document.querySelectorAll('[data-idx]');
    for (var i = 0; i < buttons.length; i++) {
      buttons[i].classList.toggle('on', buttons[i] === btn);
    }

    function step(now) {
      var t = Math.min(1, (now - t0) / span);
      var k = easeInOut(t);
      var next = {
        longitude: lerp(start.longitude, end.longitude, k),
        latitude: lerp(start.latitude, end.latitude, k),
        zoom: lerp(start.zoom, end.zoom, k),
        pitch: lerp(start.pitch, end.pitch, k),
        bearing: lerp(start.bearing, end.bearing, k)
      };
      view = next;
      deckgl.setProps({ viewState: next });
      progress.style.width = Math.round(t * 100) + '%';

      if (t < 1) {
        requestAnimationFrame(step);
      } else {
        flying = false;
        view = {
          longitude: end.longitude,
          latitude: end.latitude,
          zoom: end.zoom,
          pitch: end.pitch,
          bearing: end.bearing
        };
        deckgl.setProps({ viewState: view, controller: true });
      }
    }
    requestAnimationFrame(step);
  }

  var buttons = document.querySelectorAll('[data-idx]');
  for (var b = 0; b < buttons.length; b++) {
    buttons[b].addEventListener('click', function (e) {
      flyTo(Number(e.currentTarget.getAttribute('data-idx')), e.currentTarget);
    });
  }

  window.__deck = deckgl;
})();
</script>
</body>
</html>
Language: the code and this page are in English; the full tutorial and API notes for this example are in Chinese. The demo itself is the authoritative version — it runs exactly as shown.

More deck.gl examples

deck.gl · EXAMPLEAggregation Transition deck.gl · EXAMPLEAnimation Frame deck.gl · EXAMPLEArc