原生js实现三维可拖动旋转的卡通地球效果代码
代码语言:html
所属分类:三维
代码描述:原生js实现三维可拖动旋转的卡通地球效果代码,纯原生js编写,没有使用three引擎。
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Pirata+One&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