原生js实现三维可拖动旋转的卡通地球效果代码

代码语言:html

所属分类:三维

代码描述:原生js实现三维可拖动旋转的卡通地球效果代码,纯原生js编写,没有使用three引擎。

代码标签: 原生 js 三维 拖动 旋转 卡通 地球

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
   
    <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Pirata+One&amp;display=block'>
    <style>
        body {  margin: 0;  background-color: #f7f6f2;  overflow: hidden;}.container {  display: flex;  justify-content: center;  max-height: 100vmin;  max-width: 100vmin;  margin: 0 auto;}.globe {  height: 100vmin;  width: auto;  cursor: -webkit-grab;  cursor: grab;}.globe--dragging {  cursor: -webkit-grabbing;  cursor: grabbing;}
    </style>

</head>

<body>
    <!-- partial:index.partial.html -->
    <div class="container"><canvas class="globe" width="1800" height="1600"></canvas></div>
    <!-- partial -->
    <script>
        
        const markers = [{
  name: 'Uluru',
  lat: -25,
  long: 131
},
{
  name: 'Machu Picchu',
  lat: -13,
  long: -70
},
{
  name: 'Valley of the Kings',
  lat: 25,
  long: 32
},
{
  name: 'Stonehenge',
  lat: 51,
  long: -2
}];

const $ = {
  canvas: null,
  ctx: null,
  vCenter: 820,
  scroll: {
    lat: 0,
    long: 20
  },
  markers: [],
  timing: {
    speed: 16,
    delta: 0,
    last: 0
  },
  drag: {
    start: { x: 0, y: 0 },
    force: 0,
    prevX: 0,
    isDragging: false
  },
  colors: {
    pushPinBase: '#969799',
    pushPin: '#ed5c50',
    land: '#fac648', //'#ffc975',
    landShade: '#2c606e',
    ocean: '#2A7B96'
  },
  complexShapes: {
    // put complex shapes here
  }
}

const lerp = (norm, min, max) => {
  return (max - min) * norm + min;
}

const norm = (value, min, max) => {
  return (value - min) / (max - min);
}

const map = (value, sourceMin, sourceMax, destMin, destMax) => {
  return lerp(norm(value, sourceMin, sourceMax), destMin, destMax);
}

const dragMove = (e) => {
  if($.drag.isDragging) {
    let long = $.drag.start.long,
        clientX = e.targetTouches ? e.targetTouches[0].clientX : e.clientX,
        change = clientX - $.drag.start.x,
        prevChange = clientX - $.drag.prevX,
        canvasWidth = $.canvas.getBoundingClientRect().width;
    
    long += map(change, 0, canvasWidth, 0, 200);
    
    while(long < 0) {
      long += 360;
    } 
    
    if(prevChange > 0 && $.drag.force < 0) {
      $.drag.force = 0;
    } else if(prevChange < 0 && $.drag.force > 0) {
      $.drag.force = 0;
    }
    
    $.drag.force += prevChange * (600 / canvasWidth);
    $.drag.prevX = clientX;
    $.scroll.long = Math.abs(long) % 360;
  }
}

const dragStart = (e) => {
  if (e.targetTouches) {
    e.preventDefault();
		$.drag.start = {
      x: e.targetTouches[0].clientX,
      y: e.targetTouches[0].clientY,
      long: $.scroll.long
    };
  } else {
    $.drag.start = {
      x: e.clientX,
      y: e.clientY,
      long: $.scroll.long
    };
  }
  $.timing.speed = 0;
  $.drag.isDragging = true;
  $.canvas.classList.add('globe--dragging');
}

const dragEnd = (e) => {
  if($.drag.isDragging) {
    $.timing.speed = map($.drag.force, 0, 220, 0, 40);
    $.drag.isDragging = false;
    $.canvas.classList.remove('globe--dragging');
  }
}

const getRadius = (latitude) => {
  let yPart = Math.PI*2,
      radius = 600,
      frame = map(latitude, 90, -90, 0, 1.65);
  
  return Math.max(Math.sin(yPart + frame) * radius, 0);
}

const latLongSphere  = (lat, lon, radius) => {
    let x = 900,
        y = $.vCenter,
        z = 0;

    lon = -lon;
    let phi = (90-lat) * (Math.PI/180),
    teta = (lon + 180) * (Math.PI/180);

    x -= -(radius * Math.sin(phi) * Math.cos(teta));
    y -= radius * Math.cos(phi);
    z = radius * Math.sin(phi) * Math.sin(teta);

    return {
      x, y, z
    };
}

const drawGlobe = (ctx, color) => {
  ctx.beginPath();
  ctx.arc(900, $.vCenter, 600, 0, 2 * Math.PI);
  ctx.closePath();
  ctx.fillStyle = color;
  ctx.fill();
}

const getLandMassPaths = (name, radius, thickness) => {
  let landmassBasic = continents[name],
      landmass = null,
      first = true,
      rotated = false,
      paths = {
        ground: new Path2D(),
        top: new Path2D(),
        sections: [],
        isVisible: false
      },
      section = {
        ground: [],
        top: []
      };

    // Complexify
    if($.complexShapes[name]) {
      landmass = $.complexShapes[name];
    } else {
      landmass = complexify(landmassBasic, 1);
      $.complexShapes[name] = landmass;
    }
    
    for (let i = 0; i < landmass.length; i++) {
      let point = landmass[0],
          p = latLongSphere(point.lat + $.scroll.lat, point.long + $.scroll.long, radius);
      
        if(p.z < 0) {
          landmass.splice(0, 0, landmass.pop());
          rotated = true;
        } else {
          break;
        }
    }

    let drawCurve = false,
        curveStart = null,
        sectionIsVisible = false;

    landmass.forEach((point) => {
      let p = latLongSphere(point.lat + $.scroll.lat, point.long + $.scroll.long, radius),
          p2 = latLongSphere(point.lat + $.scroll.lat, point.long + $.scroll.long, radius + thickness);
      
      if(!sectionIsVisible && p.z > -200) {
        sectionIsVisible = true;
      }
      
      section.ground.push({
        x: p.x,
        y: p.y,
        z: p.z
      });
      section.top.push({
        x: p2.x,
        y: p2.y,
        z: p2.z
      });

      if(point.edge && !first) {
        if(sectionIsVisible) {
          paths.sections.push(Object.assign({}, section));
        }
        
        section = {
          ground: [{x: p.x, y: p.y, z: p.z }],
          top: [{x: p2.x, y: p2.y, z: p2.z }]
        };
        
        sectionIsVisible = false;
      }
      
      first = false;

      if(p.z > 0) {
        if(drawCurve) {
            drawCurve = false;
            closeCurve(paths.ground, curveStart, p, radius);
            closeCurve(paths.top, curveStart, p2, radius + thickness);
          } else {
            paths.ground.lineTo(p.x, p.y);
            paths.top.lineTo(p2.x, p2.y);
            paths.isVisible = true;
          }
      } else {
        // draw curve
        if(!drawCurve) {   
          drawCurve = true;
          curveStart = {
            x: p.x,
            y: p.y,
            z: p.z
          };
        }
      }
    });

    // if the last point is in a curve
    if(drawCurve) {
      drawCurve = false;
      let point = landmass.slice(-1)[0],
          p = latLongSphere(point.lat + $.scroll.lat, point.long + $.scroll.long, radius),
          p2 = latLongSphere(point.lat + $.scroll.lat, point.long + $.scroll.long, radius + thickness);
      
      closeCurve(paths.ground, curveStart, p, radius);
      closeCurve(paths.top, curveStart, p2, radius + thickness);
    }
    
    let p = latLongSphere(landmass[0].lat + $.scroll.lat, landmass[0].long + $.scroll.long, radius),
        p2 = latLongSphere(landmass[0].lat + $.scroll.lat, landmass[0].long + $.scroll.long, radius + thickness);  
    
    section.ground.push({
        x: p.x,
        y: p.y,
        z: p.z
      });
      section.top.push({
        x: p2.x,
        y: p2.y,
        z: p2.z
      });
  
    if(section) {
      paths.sections.push(Object.assign({}, section));
    }
    
    return paths;
}

const closeCurve = (path, curveStart, p, radius) => {
    // draw curve from curveStart på p
    let a1 = getAngle({ x: 900, y: $.vCenter}, curveStart),
        a2 = getAngle({ x: 900, y: $.vCenter}, p),
        compare = a1 - a2,
        startAngle = a1 * (Math.PI/180),
        endAngle = a2 * (Math.PI/180);
  
    path.arc(900, $.vCenter, radius, startAngle, endAngle, compare > 0 && compare < 180);
  }

const getCirclePoint = (angle, radius) => {
  let radian = (angle / 180) * Math.PI;
  
  return {
    x: radius * Math.cos(radian) + 900,
    y: radius * Math.sin(radian) + 800
   }
}

const getAngle = (p1, p2) => {
  let dy = p2.y - p1.y,
      dx = p2.x - p1.x,
      theta = Math.atan2(dy, dx);
  theta *= 180 / Math.PI;
  return theta;
}

const complexify = (landmass, level) => {
  let complex = [];
  
  for (let i = 0; i < (landmass.length - 1); i++) {
    let p1 = landmass[i],
        p2 = landmass[i + 1],
        steps = Math.floor(distanceBetween(p1, p2) / level);

    p1.edge = true;
    complex.push(p1);

    if(steps > 0) {
      let s = Math.floor(100 / steps);
      
      for (let i = 1; i <= steps; i++) {
        let percentage = i * s;
        
        if(percentage <= 100) {
          let p = {
            lat: map(percentage, 0, 100, p1.lat, p2.lat),
            long: map(percentage, 0, 100, p1.long, p2.long)
          }
          
          complex.push(p);
        }
      }
    }
  }
  
  let last = landmass.pop();
  last.edge = true;
  complex.push(last);

  return complex;
}

const distanceBetween = (.........完整代码请登录后点击上方下载按钮下载查看

网友评论0