当前位置:网站首页>商业技术解决方案与高阶技术专题 - 数据可视化专题

商业技术解决方案与高阶技术专题 - 数据可视化专题

2022-08-09 10:35:00 yanling.zhang

什么是数据可视化? 把数据经过一定转换之后变成图形显示的操作就是数据可视化。

数据类型:
定量数据:连续性变量,离散变量。
定性数据:文本描述,有序变量和无序变量。

如何把数据转换成图像?有2个步骤:

  1. 执行映射数据与不同标度对应
  2. 选择合适的图形来状映射体现出来。
坐标轴:

坐标轴是数据可视化的第一步,不同轴向展示数据标度
常见的坐标轴是直角坐标轴:
极坐标轴

颜色标度
前端数据可视化方案

将数据以图形的方式展示。

canvas

1.绘制直线
<style> canvas{
       display: block; margin: 10px auto 0; border: 1px solid orange; } </style>
</head>
<body>
  <canvas id="canvas" width="600" height="500">您的浏览器不支持canvas</canvas>

  <script> const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') // 在beginPath和closePath之间完成绘制 // 开始绘制 ctx.beginPath() ctx.lineWidth = 4 // 线条宽度 ctx.strokeStyle = 'orange' // 线条颜色 // 起点 终点 中间点 ctx.moveTo(100, 100) // 起点 ctx.lineTo(300, 200) // 中间点 ctx.lineTo(300, 300) // 终点 ctx.stroke() // 绘制一条路径 // 合并路径 ctx.closePath() </script>
</body>
2.高清绘制

为什么有时候绘制的图会模糊?
利用canvas绘制的图形并不是矢量图,因此在高清屏幕下会出现模糊的现象。

获取屏幕分辨率

window.devicePixelRatio

高清绘制


    const getPixelRatio = (context) => {
    
      return window.devicePixelRatio || 1
    }
    /* * 1. 放大canvas * 2. 在长沙市里面讲宽高设置为原来的大小 * 3. 考虑到内容的缩放,因此也需要将ctx缩放 */
   const ratio = getPixelRatio()
   const oldWidth = canvas.width
   const oldHeight = canvas.height

   canvas.width = canvas.width * ratio
   canvas.height = canvas.width * ratio

   canvas.style.width = oldWidth
   canvas.style.height = oldHeight

   ctx.scale(ratio, ratio)

3. canvas绘制直角坐标系

<style>
    canvas{
    
      display: block;
      margin: 10px auto 0;
      border: 1px solid orange;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="600" height="500">您的浏览器不支持canvas</canvas>

  <script>
    const canvas = document.getElementById('canvas')
    const ctx = canvas.getContext('2d')

    // 高清显示
    canvas.style.width = canvas.width + 'px'
    canvas.style.height = canvas.height + 'px'

    canvas.width = canvas.width * 1.5
    canvas.height = canvas.height * 1.5

    // 提前设置相关属性
    const ht = canvas.clientHeight
    const wd = canvas.clientHeight
    console.log(ht, wd)
    const pad = 20
    const bottomPad = 20
    const step = 100
    
    // 绘制轴线
    ctx.beginPath()
    ctx.lineWidth = 2
    ctx.strokeStyle = 'lightblue'
    ctx.moveTo(pad, pad) // 起点
    ctx.lineTo(pad, ht * 1.5 - bottomPad) // 绘制x轴
    ctx.lineTo(wd * 1.5 - pad, ht * 1.5 - bottomPad) // 绘制y轴
    ctx.stroke()
    ctx.closePath()

    


  const drawAxis = function(options) {
    
    const {
     ht, wd, pad, bottomPad, step, ctx } = options
    // 绘制x轴方向刻度
    ctx.beginPath()
    ctx.lineWidth = 1
    ctx.strokeStyle = '#666'
    for (let i = 0; i < Math.floor(wd * 1.5 / step); i++) {
    
      ctx.moveTo(pad + i * step, ht * 1.5 - bottomPad)
      ctx.lineTo(pad + i * step, ht * 1.5 - bottomPad - 10)
    }
    ctx.stroke()
    ctx.closePath()

    // 绘制y轴方向刻度
    ctx.beginPath()
    ctx.lineWidth = 1
    ctx.strokeStyle = '#666'
    for (let i = 0; i < Math.floor(ht * 1.5 / step); i++) {
    
      ctx.moveTo(pad, (ht * 1.5 - bottomPad) - (i * step))
      ctx.lineTo(pad + 10, (ht * 1.5 - bottomPad) - (i * step))
    }
    ctx.stroke()
    ctx.closePath()
  }

  drawAxis({
     ht: ht, wd: wd, pad: pad, bottomPad: bottomPad, step: step, ctx: ctx })
  </script>
</body>
4. canvas绘制直方图,圆形
<style>
    canvas{
    
      display: block;
      margin: 10px auto 0;
      border: 1px solid orange;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="600" height="500">您的浏览器不支持canvas</canvas>

  <script>
    const canvas = document.getElementById('canvas')
    const ctx = canvas.getContext('2d')

    // 高清显示
    canvas.style.width = canvas.width + 'px'
    canvas.style.height = canvas.height + 'px'

    canvas.width = canvas.width * 1.5
    canvas.height = canvas.height * 1.5

    // 提前设置相关属性
    const ht = canvas.clientHeight
    const wd = canvas.clientHeight
    console.log(ht, wd)
    const pad = 20
    const bottomPad = 20
    const step = 100
    
    // 绘制轴线
    ctx.beginPath()
    ctx.lineWidth = 2
    ctx.strokeStyle = 'lightblue'
    ctx.moveTo(pad, pad) // 起点
    ctx.lineTo(pad, ht * 1.5 - bottomPad) // 绘制x轴
    ctx.lineTo(wd * 1.5 - pad, ht * 1.5 - bottomPad) // 绘制y轴
    ctx.stroke()
    ctx.closePath()

    


  const drawAxis = function(options) {
    
    const {
     ht, wd, pad, bottomPad, step, ctx } = options
    // 绘制x轴方向刻度
    ctx.beginPath()
    ctx.lineWidth = 1
    ctx.strokeStyle = '#666'
    for (let i = 0; i < Math.floor(wd * 1.5 / step); i++) {
    
      ctx.moveTo(pad + i * step, ht * 1.5 - bottomPad)
      ctx.lineTo(pad + i * step, ht * 1.5 - bottomPad + 10)
    }
    ctx.stroke()
    ctx.closePath()

    // 绘制y轴方向刻度
    ctx.beginPath()
    ctx.lineWidth = 1
    ctx.strokeStyle = '#666'
    for (let i = 0; i < Math.floor(ht * 1.5 / step); i++) {
    
      ctx.moveTo(pad, (ht * 1.5 - bottomPad) - (i * step))
      ctx.lineTo(pad + 10, (ht * 1.5 - bottomPad) - (i * step))
    }
    ctx.stroke()
    ctx.closePath()
  }

  drawAxis({
     ht: ht, wd: wd, pad: pad, bottomPad: bottomPad, step: step, ctx: ctx })


  // 绘制矩形: 1. 描边 + 填充 2. 描边 3. 填充
  // ctx.beginPath()
  // ctx.lineWidth = 5
  // ctx.strokeStyle = 'orange' // 描边颜色
  // ctx.fillStyle = 'hotpink' // 填充颜色
  // // rect: 绘制矩形rect(x,y, w, h)
  // ctx.rect(100, 100, 300, 200)
  // // fill和stroke同时绘制的时候,先使用fill,再使用stroke。
  // ctx.fill() // 填充
  // ctx.stroke() // 绘制描边 
  // ctx.closePath()


  // 描边
  // ctx.beginPath()
  // ctx.lineWidth = 4
  // ctx.strokeStyle = 'seagreen'
  // ctx.rect(400, 400, 100, 100)
  // ctx.stroke()
  // ctx.closePath()

  // 填充
  // ctx.beginPath()
  // ctx.lineWidth = 4
  // ctx.strokeStyle = 'seagreen'
  // ctx.rect(600, 600, 100, 100)
  // ctx.fill()
  // ctx.closePath()

  // 绘制直方图
  ctx.beginPath()
  for (var i = 1; i < Math.floor(wd * 1.5 / step); i++) {
    
    const height = Math.random() * 300 + 50
    ctx.fillStyle ='#' + parseInt(Math.random() * 0xFFFFFF).toString(16)
    ctx.fillRect(i * step, ht * 1.5 - bottomPad - height, 40, height) // 绘制填充的矩形
  }
  ctx.closePath()

  // 绘制圆环
  ctx.beginPath()
  ctx.lineWidth = 2
  ctx.strokeStyle = 'orange'
  // arc(x, y, r, 起始角度,结束角度,时针方向)
  // ctx.arc(400, 300, 200, 0, Math.PI * 2)
  // ctx.arc(400, 300, 200, 0, Math.PI / 4, false) // 顺时针,默认顺时针。绘制到1/4 pai
  ctx.arc(400, 300, 200, 0, Math.PI / 4, true) // 逆时针
  ctx.stroke()
  ctx.closePath()

  // 绘制圆形
  ctx.beginPath()
  ctx.fillStyle = 'skyblue'
  ctx.moveTo(400, 300)
  ctx.arc(400, 300, 100, 0, Math.PI * 2)
  ctx.fill()
  ctx.closePath()
  </script>
</body>

arc:
请添加图片描述
rect:
请添加图片描述

fillRect:
请添加图片描述

绘制饼状图
<body>
  <canvas id="canvas" width="600" height="500">您的浏览器不支持canvas</canvas>

  <script>
    const canvas = document.getElementById('canvas')
    const ctx = canvas.getContext('2d')

    // 高清显示
    canvas.style.width = canvas.width + 'px'
    canvas.style.height = canvas.height + 'px'

    canvas.width = canvas.width * 1.5
    canvas.height = canvas.height * 1.5

    // 提前设置相关属性
    const ht = canvas.clientHeight
    const wd = canvas.clientHeight
    console.log(ht, wd)
    const pad = 20
    const bottomPad = 20
    const step = 100
    
    // 绘制轴线
    ctx.beginPath()
    ctx.lineWidth = 2
    ctx.strokeStyle = 'lightblue'
    ctx.moveTo(pad, pad) // 起点
    ctx.lineTo(pad, ht * 1.5 - bottomPad) // 绘制x轴
    ctx.lineTo(wd * 1.5 - pad, ht * 1.5 - bottomPad) // 绘制y轴
    ctx.stroke()
    ctx.closePath()


  const drawAxis = function(options) {
    
    const {
     ht, wd, pad, bottomPad, step, ctx } = options
    // 绘制x轴方向刻度
    ctx.beginPath()
    ctx.lineWidth = 1
    ctx.strokeStyle = '#666'
    for (let i = 0; i < Math.floor(wd * 1.5 / step); i++) {
    
      ctx.moveTo(pad + i * step, ht * 1.5 - bottomPad)
      ctx.lineTo(pad + i * step, ht * 1.5 - bottomPad + 10)
    }
    ctx.stroke()
    ctx.closePath()

    // 绘制y轴方向刻度
    ctx.beginPath()
    ctx.lineWidth = 1
    ctx.strokeStyle = '#666'
    for (let i = 0; i < Math.floor(ht * 1.5 / step); i++) {
    
      ctx.moveTo(pad, (ht * 1.5 - bottomPad) - (i * step))
      ctx.lineTo(pad + 10, (ht * 1.5 - bottomPad) - (i * step))
    }
    ctx.stroke()
    ctx.closePath()
  }

  drawAxis({
     ht: ht, wd: wd, pad: pad, bottomPad: bottomPad, step: step, ctx: ctx })

  // 绘制饼状图
  // 第一个圆弧
  ctx.beginPath()

  // 添加阴影彰显层次
  ctx.shadowOffsetX = 0
  ctx.shadowOffsetY = 0
  ctx.shadowBlur = 4
  ctx.shadowColor = '#333'

  ctx.fillStyle = '#5c1918'
  ctx.moveTo(400, 300)
  ctx.arc(400, 300, 100, - Math.PI / 2, - Math.PI / 4)
  ctx.fill()
  ctx.closePath()

  // 第2个圆弧
  ctx.beginPath()
  ctx.shadowOffsetX = 0
  ctx.shadowOffsetY = 0
  ctx.shadowBlur = 4
  ctx.shadowColor = '#5c1918'
  ctx.fillStyle = '#a32d29'
  ctx.moveTo(400, 300)
  ctx.arc(400, 300, 110, - Math.PI / 4, Math.PI / 4)
  ctx.fill()
  ctx.closePath()

  // 第3个圆弧
  ctx.beginPath()
  ctx.shadowOffsetX = 0
  ctx.shadowOffsetY = 0
  ctx.shadowBlur = 4
  ctx.shadowColor = '#a32d29'
  ctx.fillStyle = '#b9332e'
  ctx.moveTo(400, 300)
  ctx.arc(400, 300, 120, Math.PI / 4, Math.PI * 5 / 8)
  ctx.fill()
  ctx.closePath()

  // 第4个圆弧
  ctx.beginPath()
  ctx.shadowOffsetX = 0
  ctx.shadowOffsetY = 0
  ctx.shadowBlur = 4
  ctx.shadowColor = '#b9332e'
  ctx.fillStyle = '#842320'
  ctx.moveTo(400, 300)
  ctx.arc(400, 300, 130, Math.PI * 5 / 8, Math.PI)
  ctx.fill()
  ctx.closePath()

  // 第5个圆弧
  ctx.beginPath()
  ctx.shadowOffsetX = 0
  ctx.shadowOffsetY = 0
  ctx.shadowBlur = 4
  ctx.shadowColor = '#842320'
  ctx.fillStyle = '#d76662'
  ctx.moveTo(400, 300)
  ctx.arc(400, 300, 140, Math.PI, Math.PI * 3 / 2)
  ctx.fill()
  ctx.closePath()
  </script>
</body>
canvas绘制文字

fillText:
请添加图片描述

strokeText
请添加图片描述

// 文字设置
ctx.fillStyle = 'orange'
ctx.font = 'bold 60px 微软雅黑'
ctx.fillText('拉钩教育', 100, 100) // 实心文字
ctx.fillText('拉钩教育', 100, 100, 100) // 拉伸文字
ctx.strokeText('拉钩教育', 100, 240) // 描边文字

// 对其属性设置
ctx.textAlign = 'center' // 水平方向对其 left, right, center, start, end
ctx.textBaseline = 'top' // 垂直方向对其 // top bottom middle
ctx.strokeText('拉钩教育', 100, 240)
碰撞检测

svg

rect标签:创建矩形, 以及矩形的变种
  1. width:矩形的宽度,height:矩形的高度
  2. style属性用来定义css属性:
  3. css的fill属性定义矩形的填充颜色。
  4. css的stroke-width:定义边框的宽度
  5. css的stroke属性定义矩形边框的颜色。
circle标签:创建一个圆

cx,cy定义圆点的x和y坐标。如果省略 cx 和 cy,圆的中心会被设置为 (0, 0)

ellipse标签:创建椭圆

cx:定义圆点的x坐标。
xy:定义圆点的 y 坐标
rx:水平半径
ry:垂直半径

line:创建线条

x1:在x轴线条的开始
y1:在y轴线条的开始
x2:在x轴线条的结束
y2:在y轴线条的结束

polygon标签:创建多边形

points 属性定义多边形每个角的 x 和 y 坐标

polyline 标签:创建折线

points 定义折线的位置

path 标签:用来定义路径。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>svg绘制图形</title>
</head>
<body>
  <svg width="1000" height="600">
    <!-- 1.绘制矩形 -->
    <!-- <rect x="40" y="40" width="300" height="200" style="fill:orange; stroke:pink;stroke: width 4px;"></rect> -->
    
    <!-- 2.绘制圆角矩形 -->
    <!-- <rect x="40" y="40" width="300" rx="40" ry="40" height="200" style="fill:orange; stroke:pink;stroke: width 4px;"></rect> -->

    <!-- 3.绘制圆形 -->
    <!-- <circle cx="200" cy="200" r="100" style="fill:darkblue"></circle> -->

    <!-- 4.绘制椭圆 -->
    <!-- <ellipse cx="200" cy="200" rx="80" ry="40" style="fill: seagreen"></ellipse> -->

    <!-- 5.绘制线条 -->
    <!-- <line x1="100" y1="40" x2="500" y2="60" style="stroke: darkcyan;stroke-width: 4px"></line> -->

    <!-- 6.多边形的绘制(以三角形为例子) -->
    <!-- transform:位移 -->
    <!-- <polygon points="200, 40, 400, 100, 100, 90" style="fill: darkkhaki; stroke: width 2px; stroke:darksalmon;" transform="translate(100, 100)"></polygon> -->
	
	<!-- 7.折线 -->
	<polyline points="0,0 0,20 20,20 20,40 40,40 40,60" style="fill:white;stroke:red;stroke-width:2"/>
  
    <!-- 8.绘制文字 -->
    <text x="200" y="200" style="fill: deepskyblue; font-size: 30;" textLength="200">拉钩教育</text>
  </svg>
</body>
</html>

D3.js

官方地址:https://d3js.org/
中文官网 https://github.com/d3/d3/wiki/CN-Home

d3提过了一些api对svg的元素进行动态修改。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>svg绘制图形</title>
  <script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
  <script>
    // 选择元素
    const data = [100, 20, 30, 50]
    // select:
    d3.select('body') // 选中元素
      .selectAll('p')
      .data(data) // 绑定数据
      .enter() // 新增标签
      .append('p') // 把p元素添加到页面上
      .text('教育') // p标签写入文字
  </script>
</body>
</html>
D3操作svg
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
  <div id="box">
    <p>p元素</p>
    <p>p元素</p>
    <p>p元素</p>
    <div>我是一个div</div>
  </div>
  <svg width="600" height="400">
    <rect x="100" y="100" width="300" height="200" style="stroke:red;stroke: width 4px;"></rect>
  </svg>
  <script>
    // 1.获取dom元素
    console.log(d3.select('p')) // 不管有几个p元素,只获取第一个p元素
    console.log(d3.select('#box p')) // 不管有几个p元素,只获取第一个p元素
    console.log(d3.selectAll('p')) // 获取所有的p元素

    // 2.获取元素属性
    console.log(typeof d3.select('rect').attr('width')) // string

    // 3.设置属性 链式调用的风格
    d3.select('rect')
    .attr('fill', 'seagreen') // 属性设置
    .attr('transform', 'translate(100, 100)') // 属性设置

    // 4.添加,删除元素
    d3.select('svg')
      .append('rect') // 添加rect元素
      .attr('x', '100')
      .attr('y', '200')
      .attr('width', '200')
      .attr('height', '200')
      .attr('fill', 'red')
    
      d3.select('svg')
        .append('text') // 添加text标签
        .attr('x', '10')
        .attr('y', '50')
        .attr('fill', 'red')
        .attr('font-size', 20)
        .attr('textLength', '200') // 设置text的长度
        .text('拉钩教育') // 在text标签里面写入 ‘拉钩教育’文字 

     // 5.元素删除
     d3.selectAll('rect')
      .remove() // 删除。但是一般不用这个属性删除,设置opcity为0,为了方便调试。

  </script>
</body>
</html>
D3数据操作

update:更新。元素的个数和数据集的关系:一一对应。
enter: 新增。有数据但是页面中没有元素。例如:定义的data有4条数据,但是页面中只有2个元素,默认执行新增元素。
exit:删除。页面中有元素但是没有数据。一般对应删除的操作。

请添加图片描述

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
  <script>
   // 1.添加svg
   d3.select('body').append('svg').attr('width', 600).attr('height', 400)

  // 2.绘制圆形1
  d3.select('svg').append('circle')
    .attr('cx', '100')
    .attr('cy', '100')
    .attr('r', '10')
    .attr('fill', 'orange')

    d3.select('svg').append('circle')
    .attr('cx', '120')
    .attr('cy', '120')
    .attr('r', '20')
    .attr('fill', 'seagreen')

    // 3.同时创建多个元素
    // 定义数据
    const data = [
      {
     cx: '100', cy: '100', r: '10', fill: 'orange' },
      {
     cx: '130', cy: '140', r: '20', fill: 'orange' },
      {
     cx: '230', cy: '240', r: '19', fill: 'orange' },
    ]

    d3.select('svg')
      .selectAll('circle')
      .data(data) // 绑定数据
      .enter() // 添加元素
      .append('circle')
      .attr('cx', (d) => d.cx)
      .attr('cy', (d) => d.cy)
      .attr('r', (d) => d.r)
      .attr('fill', (d) => d.fill)


  </script>
</body>
</html>
update-enter-exit
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
  <p></p>

  <script>
  const data = [1,2,3,4,5,6,7]
  const allAp = d3.selectAll('body p')
  
  // 1.更新元素 - update
  // const update = allAp.data(data)
  // update.text(d => '更新的操作' + d)
 
  // 2.新增元素 - enter
  // const enter = update.enter()
  // .append('p')
  // .text(d => '新增' + d)

  // 3.删除元素 - exit
  // const update = allAp.data(data)
  // const exit = update.exit()
  // exit.text(d => '将要删除' + d)

  // data和datum的区别
  // data只能绑定数组格式的数据
  // datum可以绑定字符串格式的,对象格式的数据,他内部会把这个转换为数组格式的

  allAp.datum(data).text(d => d)
  allAp.data(data).text(d => d)



  </script>
</body>
</html>

请添加图片描述

绘制静态直方图
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>绘制直方图</title>
  <script src="http://d3js.org/d3.v3.min.js"></script>
  <style>
    div svg{
    
      display: block;
      margin: 40px solid orange;
      border: 1px solid orange;
    }
  </style>
</head>
<body>
<div id="svg"></div>
<script>
  // 定义数据
  const width = 700
  const height = 400
  const rectStep = 40 // 步长,矩形和矩形之间的距离
  const rectWidth = 30
  const data = [10, 50, 280, 122, 90, 230, 250, 300]

  // 定义填充
  const margin = {
     left: 20, right: 20, top: 20, bottom: 20 }

  // 创建svg
  d3.select('#svg')
    .append('svg')
    .attr('width', width)
    .attr('height', height)

  // 绘制图形
  d3.select('svg')
  .selectAll('rect')
  .data(data)
  .enter()
  .append('rect')
  .attr('x', (d, i)=> i * rectStep + margin.left)
  .attr('y', (d) => height - d - margin.bottom)
  .attr('width', rectWidth)
  .attr('height', d => d)
  .attr('fill', 'lightblue')

  // 绘制文字
  d3.select('svg')
  .selectAll('text')
  .data(data)
  .enter()
  .append('text')
  .attr('fill', '#333')
  .attr('font-size', '15')
  .attr('x', (d, i)=> i * rectStep + margin.left)
  .attr('y', (d) => height - d - margin.bottom)
  .attr('text-anchor', 'middle') // 文字对齐方式
  .attr('transform', `translate(${
      rectWidth / 2})`)
  .text(d => d)
</script>
</body>
</html>
线性比例尺
  // 比例尺:将某一个区域的值映射到另一个区域,保持它的大小关系不变
  // 比例尺:线性,序列
  // x y
  // x 定义域,输入
  // y 值域 输出
  const data = [10, 50, 280, 122, 90, 230, 250, 300]

  const min = d3.min(data)
  const max = d3.max(data)

  // 当我们有了定义域,之后,就可以让d3来完成输出
 const linear = d3.scaleLinear()
  .domain([min, max])
  range([0, 300])

console.log(linear)

比例尺与坐标轴

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>比例尺与坐标轴</title>
  <script src="./d3.min.js"></script>
</head>

<body>
  <div id="box"></div>
  <script>
    // 定义数据
    const width = 600
    const height = 500
    const margin = {
     left: 50, right: 50, bottom: 50, top: 50 }
    const kindData = ['ES6+', "NodeJS", "Vue", "React", "Angular"]
    const kindPixel = [margin.left, width - margin.right]
    const ratioData = [80, 60, 50, 20, 100]
    const ratioPixel = [height - margin.bottom, margin.top]

    // 设置画布
    d3.select('#box').append('svg')
      .attr('width', width)
      .attr('height', height)

    // 定义比例尺
    const xScale = d3.scaleBand().domain(kindData).rangeRound(kindPixel)
    // 定义坐标刻度生成器
    const xAxis = d3.axisBottom(xScale)
    // 绘制X轴具体的刻度内容
    d3.select('svg').append('g')
      .call(xAxis) // 绘制横轴出比例尺,但是出现在顶部
      .attr('transform', `translate(0, ${
      height - margin.bottom})`) // 向下移动横向轴的位置
      .attr('font-size', 14)

    // 定义y轴比例尺
    const yScale = d3.scaleLinear().domain([0, d3.max(ratioData)]).range(ratioPixel)
    const yAxis = d3.axisLeft(yScale)
    d3.select('svg').append('g')
      .call(yAxis)
      .attr('transform', `translate(50, 0)`)
      .attr('font-size', 14)

  </script>
</body>

</html>

请添加图片描述

D3过渡 - transition duration delay ease

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>D3过渡</title>
  <script src="./d3.min.js"></script>
</head>

<body>
  <script>
    // 添加画布
    const svg = d3.select('body').append('svg')
      .attr('width', 600)
      .attr('height', 400)

    // 绘制图形
    const circle = d3.select('svg').append('circle')
      .attr('cx', 100)
      .attr('cy', 100)
      .attr('r', 20)
      .attr('fill', 'seagreen')

    // transition duration delay ease 
    // 初始状态 结束状态 
    circle.attr('cx', 100).attr('cy', 100)

    // 结束状态
    circle.transition()
      .duration(3000)
      .delay(1000)
      .ease(d3.easeBounce)
      .attr('cx', 500)
      .attr('cy', 300)
  </script>
</body>

</html>

过渡直方图1

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>柱状图带过渡</title>
  <script src="./d3.min.js"></script>
</head>

<body>
  <script>
    // 画布大小
    const width = 600
    const height = 400

    // 1 添加画布
    const svg = d3.select('body').append('svg')
      .attr('width', width)
      .attr('height', height)

    // 2 填充
    const margin = {
     left: 30, right: 30, top: 20, bottom: 30 }

    // 3 准备源数据
    const data = [10, 20, 30, 40, 36, 25, 18, 5]

    // 4 绘制坐标轴(比例尺)[0, 1 , 2, 3]=>[0, 540]
    const xScale = d3.scaleBand()
      .domain(d3.range(data.length))
      .range([margin.left, width - margin.left - margin.right])
      .padding(0.1) // 如果不设置每根柱子之间是紧挨着的

    // 5 定义X轴的生成器
    const xAxis = d3.axisBottom(xScale)
    // 6 绘制X轴坐标
    const gx = d3.select('svg').append('g')
      .call(xAxis)
      .attr('transform', `translate(0, ${
      height - margin.bottom})`)

    // 7 绘制Y轴(比例尺 生成器 Y绘制)[5, 40] [30, 400]
    const yScale = d3.scaleLinear()
      .domain([0, d3.max(data)])
      .range([height - margin.top - margin.bottom, margin.bottom])
    const yAxis = d3.axisLeft(yScale)
    const gy = d3.select('svg').append('g')
      .call(yAxis)
      .attr('transform', `translate(${
      margin.left}, ${
      margin.top})`)

    // 8 绘制柱状图
    const rects = svg.selectAll('.myRect')
      .data(data)
      .enter()
      .append('rect')
      .attr('class', 'myRect')
      .attr('x', (d, i) => xScale(i))
      .attr('y', d => yScale(d))
      .attr('width', xScale.bandwidth())
      .attr('height', d => yScale(0) - yScale(d))
      .attr('fill', 'orange')
      .attr('transform', `translate(0, ${
      margin.top})`)

    // 提供二个状态
    rects.attr('y', () => yScale(0)).attr('height', 0)
    rects.transition()
      .duration(1000)
      .delay((d, i) => i * 200)
      .ease(d3.easeBounce)
      .attr('y', d => yScale(d))
      .attr('height', d => yScale(0) - yScale(d))

    // 9 绘制文件
    const texts = svg.selectAll('myText')
      .data(data)
      .enter()
      .append('text')
      .attr('class', 'myText')
      .attr('fill', '#666')
      .attr('text-anchor', 'middle')
      .attr('x', (d, i) => xScale(i))
      .text(d => d)
      .attr('transform', `translate(${
      xScale.bandwidth() / 2}, ${
      margin.top})`)
      .attr('y', () => yScale(0))
      .transition()
      .delay((d, i) => i * 200)
      .duration(1000)
      .ease(d3.easeBounce)
      .attr('y', (d) => yScale(d) - 5)
  </script>
</body>

</html>

请添加图片描述

直方图添加提示

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>柱状图带交互</title>
  <style>
    html,
    body {
    
      width: 100%;
      margin: 0;
    }

    #tip {
    
      color: #fff;
      display: none;
      margin-top: 15px;
      margin-left: 15px;
      position: absolute;
      padding: 5px 10px;
      border-radius: 3px;
      background: rgba(0, 0, 0, .4);
      font: normal 14px/1em '微软雅黑';
    }
  </style>
  <script src="./d3.min.js"></script>
</head>

<body>
  <script>
    // 画布大小
    const width = 600
    const height = 400

    // 1 添加画布
    const svg = d3.select('body').append('svg')
      .attr('width', width)
      .attr('height', height)

    // 2 填充
    const margin = {
     left: 30, right: 30, top: 20, bottom: 30 }

    // 3 准备源数据
    const data = [10, 20, 30, 40, 36, 25, 18, 5]

    // 4 绘制坐标轴(比例尺)[0, 1 , 2, 3]=>[0, 540]
    const xScale = d3.scaleBand()
      .domain(d3.range(data.length))
      .range([margin.left, width - margin.left - margin.right])
      .padding(0.1)

    // 5 定义X轴的生成器
    const xAxis = d3.axisBottom(xScale)
    // 6 绘制X轴坐标
    const gx = d3.select('svg').append('g')
      .call(xAxis)
      .attr('transform', `translate(0, ${
      height - margin.bottom})`)

    // 7 绘制Y轴(比例尺 生成器 Y绘制)[5, 40] [30, 400]
    const yScale = d3.scaleLinear()
      .domain([0, d3.max(data)])
      .range([height - margin.top - margin.bottom, margin.bottom])
    const yAxis = d3.axisLeft(yScale)
    const gy = d3.select('svg').append('g')
      .call(yAxis)
      .attr('transform', `translate(${
      margin.left}, ${
      margin.top})`)

    // 8 绘制柱状图
    const rects = svg.selectAll('.myRect')
      .data(data)
      .enter()
      .append('rect')
      .attr('class', 'myRect')
      .attr('x', (d, i) => xScale(i))
      .attr('y', d => yScale(d))
      .attr('width', xScale.bandwidth())
      .attr('height', d => yScale(0) - yScale(d))
      .attr('fill', 'orange')
      .attr('transform', `translate(0, ${
      margin.top})`)

    // 提供二个状态
    rects.attr('y', () => yScale(0)).attr('height', 0)
    rects.transition()
      .duration(1000)
      .delay((d, i) => i * 200)
      .ease(d3.easeBounce)
      .attr('y', d => yScale(d))
      .attr('height', d => yScale(0) - yScale(d))

    // 9 绘制文件
    const texts = svg.selectAll('myText')
      .data(data)
      .enter()
      .append('text')
      .attr('class', 'myText')
      .attr('fill', '#666')
      .attr('text-anchor', 'middle')
      .attr('x', (d, i) => xScale(i))
      .text(d => d)
      .attr('transform', `translate(${
      xScale.bandwidth() / 2}, ${
      margin.top})`)
      .attr('y', () => yScale(0))
      .transition()
      .delay((d, i) => i * 200)
      .duration(1000)
      .ease(d3.easeBounce)
      .attr('y', (d) => yScale(d) - 5)

    // 自定义缓动类
    class EaseObj {
    
      constructor(target) {
    
        this.target = target
        this.pos = {
     x: width / 2, y: height / 2 }
        this.endPos = {
     x: 0, y: 0 }
        this._play = false
        this.fm = 0
        this.speed = 0.1
      }
      set animate(value) {
    
        if (value !== this._play) {
    
          if (value) {
    
            this.render()
          } else {
    
            this.cancel()
          }
          this._play = value
        }
      }

      render() {
    
        const {
     pos, endPos, speed, target } = this
        pos.x += (endPos.x - pos.x) * speed
        pos.y += (endPos.y - pos.y) * speed
        target.style('left', `${
      pos.x}px`)
          .style('top', `${
      pos.y}px`)

        this.fm = requestAnimationFrame(() => {
    
          this.render()
        })
      }

      cancel() {
    
        cancelAnimationFrame(this.fm)
      }
    }

    // 10 定义提示框元素
    const tip = d3.select('body').append('div').attr('id', 'tip')
    // 11 鼠标移上
    rects.on('mouseover', ({
      clientX, clientY }, data) => {
    
      tip.style('left', `${
      clientX}px`)
        .style('top', `${
      clientY}px`)
        .style('display', 'block')
        .html(` <p>此项平均值:${
      data}</p> `)
    })

    const tipObj = new EaseObj(tip)
    rects.on('mousemove', ({
      clientX, clientY }, data) => {
    
      tipObj.endPos = {
     x: clientX, y: clientY }
      tipObj.animate = true
    })

    rects.on('mouseout', () => {
    
      tipObj.animate = false
      tip.style('display', 'none')
    })


    // rects.on('mousemove', ({ clientX, clientY }, data) => {
    
    // tip.style('left', `${clientX}px`)
    // .style('top', `${clientY}px`)
    // })

    // rects.on('mouseout', ({ clientX, clientY }, data) => {
    
    // tip.style('left', `${clientX}px`)
    // .style('top', `${clientY}px`)
    // .style('display', 'none')
    // })
  </script>
</body>

</html>

请添加图片描述

WebGL与ThreeJS

WebGL就是将javascript和opengl es2 操作结合在一起。
ThreeJS是采用javascript编写的类库。
场景,相机,渲染器,几何体
请添加图片描述
场景:就是一个显示呈现的舞台。
相机:浏览器端呈现的内容都是相机拍摄。其实就是眼睛。
渲染器:决定元素如何呈现在页面上。
几何体:

请添加图片描述

ThreeJS绘制立方体

https://threejs.org/

https://threejs.org/docs/index.html#manual/zh/introduction/Creating-a-scene

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="box"></div>
    <script>
      /** * 1.场景 * 2.相机(看到场景中的内容) * 3.渲染器(呈现到页面上) * */
        // 创建场景
        const scene = new THREE.Scene();
        // 创建相机
        const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
        // 创建渲染器
        const renderer = new THREE.WebGLRenderer({
     antialias: true }); // antialias: true抗锯齿设置
        // 设置渲染器的大小
        renderer.setSize( window.innerWidth, window.innerHeight );
         // 添加到页面上
        document.body.appendChild( renderer.domElement );

        // 创建立方体
        const geometry = new THREE.BoxGeometry(1,1,1);
        // 创建材质
        const material = new THREE.MeshBasicMaterial( {
     color: 0x00ff00, wireframe: true } ); // wireframe: true 显示几何体的线条,不是填充色
        // 创建网格
        const cube = new THREE.Mesh( geometry, material );
        scene.add( cube );
        // 更改相机的位置
        camera.position.z = 5; 

        function animate() {
    
            requestAnimationFrame( animate );

            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;

            renderer.render( scene, camera );
        };

        animate();
    </script>
</body>
</html>
  

请添加图片描述

材质与相机控制

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>轨迹球控制与材质</title>
  <script src="./three.min.js"></script>
  <script src="./TrackballControls.js"></script>
</head>

<body>
  <script>
    // 定义全局变量
    let scene, camera, geometry, mesh, renderer, controls

    // 初始化渲染器
    function initRenderer() {
    
      // 初始化渲染器
      renderer = new THREE.WebGLRenderer({
     antialias: true })
      // 设置渲染器大小
      renderer.setSize(window.innerWidth, window.innerHeight)
      // 高清展示
      renderer.setPixelRatio(window.devicePixelRatio)
      // 渲染器添加到页面
      document.body.appendChild(renderer.domElement)
    }

    // 初始化场景
    function initScene() {
    
      scene = new THREE.Scene()
      const axesHelper = new THREE.AxesHelper(100)
      // 把axesHelper添加到场景中
      scene.add(axesHelper)
    }

    // 初始化相机
    function initCamera() {
    
      // 添加透视相机
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
      // 设置相机的位置
      camera.position.set(0, 0, 15)
      // 创建轨迹
      controls = new THREE.TrackballControls(camera, renderer.domElement)
    }

    // 初始化模型
    function initMesh() {
    
      // BoxGeometry:盒子模型
      geometry = new THREE.BoxGeometry(2, 2, 2)
      // material = new THREE.MeshNormalMaterial()
      
      // TextureLoader:加载文件,加载纹理贴图
      const texture = new THREE.TextureLoader().load('./img/crate.gif')
      console.log(texture)
      // 创建材质
      material = new THREE.MeshBasicMaterial({
    
        map: texture,
        side: THREE.DoubleSide // 里外都加贴图
      })
      
      // 创建网格
      mesh = new THREE.Mesh(geometry, material)
      scene.add(mesh)
    }

    // 初始化动画
    function animate() {
    
      requestAnimationFrame(animate)
      // 更新控制器位置
      controls.update()
      renderer.render(scene, camera)
    }

    // 定义初始化方法
    function init() {
    
      initRenderer()
      initScene()
      initCamera()
      initMesh()
      animate()
    }

    init()

  </script>
</body>

</html>

请添加图片描述

光源操作

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>设置场景光</title>
  <script src="./three.min.js"></script>
  <script src="./TrackballControls.js"></script>
</head>

<body>
  <script>
    // 定义全局变量
    let scene, camera, geometry, mesh, renderer, controls

    // 初始化渲染器
    function initRenderer() {
    
      renderer = new THREE.WebGLRenderer({
     antialias: true })
      renderer.setSize(window.innerWidth, window.innerHeight)
      renderer.setPixelRatio(window.devicePixelRatio)
      document.body.appendChild(renderer.domElement)
    }

    // 初始化场景
    function initScene() {
    
      scene = new THREE.Scene()
      const axesHelper = new THREE.AxesHelper(100)
      scene.add(axesHelper)

 +     // const directionalLight = new THREE.DirectionalLight('red') // 平行光

      // const ambientLight = new THREE.AmbientLight('orange') // 全局环境光

      // const pointLight = new THREE.PointLight('green') // 点光源:从一个点向各个方向发射光源

      // const spotLight = new THREE.SpotLight('lightblue') // 聚光灯: 从一个方向向一个个点发出光

  +    const hemisphereLight = new THREE.HemisphereLight('red') // 室外的光源,光照颜色从天空光线颜色渐变到地面光线颜色。

      hemisphereLight.position.set(0, 30, 0)
      scene.add(hemisphereLight)

    }

    // 初始化相机
    function initCamera() {
    
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
      camera.position.set(0, 0, 15)
      controls = new THREE.TrackballControls(camera, renderer.domElement)
    }

    // 初始化模型
    function initMesh() {
    
      // SphereGeometry:不规则的多边形 球形状
 +     geometry = new THREE.SphereGeometry(3, 26, 26)
      const texture = new THREE.TextureLoader().load('img/crate.gif')
      
      // MeshPhongMaterial:网格材质
      material = new THREE.MeshPhongMaterial({
    
        map: texture,
        side: THREE.DoubleSide
      })
      mesh = new THREE.Mesh(geometry, material)
      scene.add(mesh)
    }

    // 初始化动画
    function animate() {
    
      requestAnimationFrame(animate)
      controls.update()
      renderer.render(scene, camera)
    }

    // 定义初始化方法
    function init() {
    
      initRenderer()
      initScene()
      initCamera()
      initMesh()
      animate()
    }

    init()

  </script>
</body>

</html>

请添加图片描述

精灵材质及交互

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>精灵材质与3D交互</title>
  <style>
    * {
    
      margin: 0;
      padding: 0;
    }

    canvas {
    
      width: 100%;
      height: 100%;
      display: block;
    }
  </style>
  <script src="./three.min.js"></script>
  <script src="./TrackballControls.js"></script>
</head>

<body>
  <script>
    // 定义全局变量
    let scene, camera, geometry, mesh, renderer, controls
	
	// Raycaster:光线投射
 +   const raycaster = new THREE.Raycaster()
 + // Vector2:二维向量
 +   const mouse = new THREE.Vector2()

 +   function onMouseMove(event) {
    

      // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)

      mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;

    }

+    window.addEventListener('mousemove', onMouseMove, false)

+    window.addEventListener('click', function () {
    
      // 计算物体和射线的焦点
      const intersects = raycaster.intersectObjects([mesh])
      if (intersects.length > 0) {
    
        mesh.rotation.x += 01
      }
    }, false)


    // 初始化渲染器
    function initRenderer() {
    
      renderer = new THREE.WebGLRenderer({
     antialias: true })
      renderer.setSize(window.innerWidth, window.innerHeight)
      renderer.setPixelRatio(window.devicePixelRatio)
      document.body.appendChild(renderer.domElement)
    }

    // 初始化场景
    function initScene() {
    
      scene = new THREE.Scene()
      const axesHelper = new THREE.AxesHelper(100)
      scene.add(axesHelper)
    }

    // 初始化相机
    function initCamera() {
    
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
      camera.position.set(0, 0, 15)
      controls = new THREE.TrackballControls(camera, renderer.domElement)
    }

    // 初始化模型
    function initMesh() {
    

+      const map = new THREE.TextureLoader().load('img/icon.png')
      const material = new THREE.SpriteMaterial({
     map: map, color: 0xffffff })
      const sprite = new THREE.Sprite(material)
      scene.add(sprite)

      // geometry = new THREE.BoxGeometry(2, 2, 2)
      // // material = new THREE.MeshNormalMaterial()
      // const texture = new THREE.TextureLoader().load('img/crate.gif')
      // material = new THREE.MeshBasicMaterial({
    
      // map: texture,
      // side: THREE.DoubleSide
      // })
      // mesh = new THREE.Mesh(geometry, material)
      // scene.add(mesh)
    }

    // 初始化动画
    function animate() {
    
      requestAnimationFrame(animate)
      controls.update()
      renderer.render(scene, camera)
    }

    // 定义初始化方法
    function init() {
    
      initRenderer()
      initScene()
      initCamera()
      initMesh()
      animate()
    }

    init()

  </script>
</body>

</html>

请添加图片描述

VR全景拼装

空间的拼接

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>轨迹球控制与材质</title>
  <style>
    * {
    
      margin: 0;
      padding: 0;
    }

    canvas {
    
      display: block;
      height: 100%;
      width: 100%;
    }
  </style>
  <script src="./three.min.js"></script>
  <script src="./TrackballControls.js"></script>
</head>

<body>
  <script>
    // 定义全局变量
    let scene, camera, geometry, mesh, renderer, controls

    // 初始化渲染器
    function initRenderer() {
    
      renderer = new THREE.WebGLRenderer({
     antialias: true })
      renderer.setSize(window.innerWidth, window.innerHeight)
      renderer.setPixelRatio(window.devicePixelRatio)
      document.body.appendChild(renderer.domElement)
    }

    // 初始化场景
    function initScene() {
    
      scene = new THREE.Scene()
      const axesHelper = new THREE.AxesHelper(100)
      scene.add(axesHelper)
    }

    // 初始化相机
    function initCamera() {
    
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
      camera.position.set(0, 0, 15)
      controls = new THREE.TrackballControls(camera, renderer.domElement)
    }

    // 初始化模型
    function initMesh() {
    

      // 前面: PlaneGeometry:平面 (宽度,高度)
      const geometryF = new THREE.PlaneGeometry(4, 4)
      const materialF = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/0_f.jpg'),
        side: THREE.DoubleSide
      })
      // Mesh:网格
      const meshF = new THREE.Mesh(geometryF, materialF)
      
      // 设置网格的位置
      meshF.rotation.y = 180 * Math.PI / 180
      meshF.position.z = 2
      
      // 把网格添加到场景中
      scene.add(meshF)

      // 后面
      const geometryB = new THREE.PlaneGeometry(4, 4)
      const materialB = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/0_b.jpg'),
        side: THREE.DoubleSide
      })
      const meshB = new THREE.Mesh(geometryB, materialB)
      // meshB.rotation.y = 180 * Math.PI / 180
      meshB.position.z = -2
      scene.add(meshB)

      // 左侧 
      const geometryL = new THREE.PlaneGeometry(4, 4)
      const materialL = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/0_l.jpg'),
        side: THREE.DoubleSide
      })
      const meshL = new THREE.Mesh(geometryL, materialL)
      meshL.rotation.y = (-90) * Math.PI / 180
      meshL.position.x = 2
      scene.add(meshL)

      // 右侧 
      const geometryR = new THREE.PlaneGeometry(4, 4)
      const materialR = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/0_r.jpg'),
        side: THREE.DoubleSide
      })
      const meshR = new THREE.Mesh(geometryR, materialR)
      meshR.rotation.y = (90) * Math.PI / 180
      meshR.position.x = -2
      scene.add(meshR)

      // 上面
      const geometryU = new THREE.PlaneGeometry(4, 4)
      const materialU = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/0_u.jpg'),
        side: THREE.DoubleSide
      })
      const meshU = new THREE.Mesh(geometryU, materialU)
      meshU.rotation.x = (90) * Math.PI / 180
      meshU.rotation.z = (180) * Math.PI / 180
      meshU.position.y = 2
      scene.add(meshU)

      // 下面
      const geometryD = new THREE.PlaneGeometry(4, 4)
      const materialD = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/0_d.jpg'),
        side: THREE.DoubleSide
      })
      const meshD = new THREE.Mesh(geometryD, materialD)
      meshD.rotation.x = (-90) * Math.PI / 180
      meshD.rotation.z = (180) * Math.PI / 180
      meshD.position.y = -2
      scene.add(meshD)
    }

    // 初始化动画
    function animate() {
    
      requestAnimationFrame(animate)
      controls.update()
      renderer.render(scene, camera)
    }

    // 定义初始化方法
    function init() {
    
      initRenderer()
      initScene()
      initCamera()
      initMesh()
      animate()
    }

    init()

  </script>
</body>

</html>

请添加图片描述

全景看房实现

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>轨迹球控制与材质</title>
  <style>
    * {
    
      margin: 0;
      padding: 0;
    }

    canvas {
    
      display: block;
      height: 100%;
      width: 100%;
    }
  </style>
  <script src="./three.min.js"></script>
  <script src="./TrackballControls.js"></script>
</head>

<body>
  <script>
    // 定义全局变量
    let scene, camera, geometry, mesh, renderer, controls
    let sixPlane = []
    let spriteArrow = ""
    let raycaster = new THREE.Raycaster()
    const mouse = new THREE.Vector2()

    function onMouseMove(event) {
    
      // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
      mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;

    }
    window.addEventListener('mousemove', onMouseMove, false)

    // 鼠标点击 
    function mouseClickEvent(ev) {
    
      ev.preventDefault();
      // 射线捕获
      raycaster.setFromCamera(mouse, camera)

      const intersects = raycaster.intersectObjects([spriteArrow])

      if (intersects.length > 0) {
    
        changeScene()
      }
    }

    window.addEventListener('click', mouseClickEvent, false)

    // 初始化渲染器
    function initRenderer() {
    
      renderer = new THREE.WebGLRenderer({
     antialias: true })
      renderer.setSize(window.innerWidth, window.innerHeight)
      renderer.setPixelRatio(window.devicePixelRatio)
      document.body.appendChild(renderer.domElement)
    }

    // 初始化场景
    function initScene() {
    
      scene = new THREE.Scene()
      const axesHelper = new THREE.AxesHelper(100)
      scene.add(axesHelper)
    }

    // 初始化相机
    function initCamera() {
    
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
      camera.position.set(0, 0, 15)
      controls = new THREE.TrackballControls(camera, renderer.domElement)
      controls.maxDistance = 2
      controls.minDistance = 0
    }

    // 初始化模型
    function initMesh() {
    

      // 利用精灵材质引入地面标记 
      new THREE.TextureLoader().load('img/icon.png', (texture) => {
    
        const spriteMaterial = new THREE.SpriteMaterial({
    
          map: texture
        })
        spriteArrow = new THREE.Sprite(spriteMaterial)
        spriteArrow.scale.set(0.1, 0.1, 0.1)
        spriteArrow.position.set(0.5, -1, -1.5)
        scene.add(spriteArrow)
      })

      sixPlane = createPlane(0)
      for (let i = 0; i < 6; i++) {
    
        scene.add(sixPlane[i])
      }
    }

    // 初始化动画
    function animate() {
    
      requestAnimationFrame(animate)
      controls.update()
      renderer.render(scene, camera)
    }

    // 定义初始化方法
    function init() {
    
      initRenderer()
      initScene()
      initCamera()
      initMesh()
      animate()
    }

    init()

    function createPlane(num) {
    
      const BoxGeometry = []
      // 前面
      const geometryF = new THREE.PlaneGeometry(4, 4)
      const materialF = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/' + num + '_f.jpg'),
        side: THREE.DoubleSide
      })
      const meshF = new THREE.Mesh(geometryF, materialF)
      meshF.rotation.y = 180 * Math.PI / 180
      meshF.position.z = 2
      BoxGeometry.push(meshF)

      // 后面
      const geometryB = new THREE.PlaneGeometry(4, 4)
      const materialB = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/' + num + '_b.jpg'),
        side: THREE.DoubleSide
      })
      const meshB = new THREE.Mesh(geometryB, materialB)
      // meshB.rotation.y = 180 * Math.PI / 180
      meshB.position.z = -2
      BoxGeometry.push(meshB)

      // 左侧 
      const geometryL = new THREE.PlaneGeometry(4, 4)
      const materialL = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/' + num + '_l.jpg'),
        side: THREE.DoubleSide
      })
      const meshL = new THREE.Mesh(geometryL, materialL)
      meshL.rotation.y = (-90) * Math.PI / 180
      meshL.position.x = 2
      BoxGeometry.push(meshL)

      // 右侧 
      const geometryR = new THREE.PlaneGeometry(4, 4)
      const materialR = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/' + num + '_r.jpg'),
        side: THREE.DoubleSide
      })
      const meshR = new THREE.Mesh(geometryR, materialR)
      meshR.rotation.y = (90) * Math.PI / 180
      meshR.position.x = -2
      BoxGeometry.push(meshR)

      // 上面
      const geometryU = new THREE.PlaneGeometry(4, 4)
      const materialU = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/' + num + '_u.jpg'),
        side: THREE.DoubleSide
      })
      const meshU = new THREE.Mesh(geometryU, materialU)
      meshU.rotation.x = (90) * Math.PI / 180
      meshU.rotation.z = (180) * Math.PI / 180
      meshU.position.y = 2
      BoxGeometry.push(meshU)

      // 下面
      const geometryD = new THREE.PlaneGeometry(4, 4)
      const materialD = new THREE.MeshBasicMaterial({
    
        map: new THREE.TextureLoader().load('img/' + num + '_d.jpg'),
        side: THREE.DoubleSide
      })
      const meshD = new THREE.Mesh(geometryD, materialD)
      meshD.rotation.x = (-90) * Math.PI / 180
      meshD.rotation.z = (180) * Math.PI / 180
      meshD.position.y = -2
      BoxGeometry.push(meshD)

      return BoxGeometry
    }

    function changeScene() {
    
      // 创建六个面 
      const sixBox = createPlane(2)
      const timer = setInterval(() => {
    
        camera.fov -= 1
        camera.updateProjectionMatrix()
        if (camera.fov == 20) {
    
          clearInterval(timer)
          camera.fov = 45
          camera.updateProjectionMatrix()
          for (let i = 0; i < 6; i++) {
    
            scene.remove(sixPlane[i])
          }
          sixPlane = sixBox
          for (let i = 0; i < 6; i++) {
    
            scene.add(sixPlane[i])
          }
          spriteArrow.visible = false
        }
      }, 50)
    }

  </script>
</body>

</html>

请添加图片描述

原网站

版权声明
本文为[yanling.zhang]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_38245947/article/details/123140176