当前位置:   article > 正文

【d3.js入门】d3-drag介绍和实战

d3-drag

效果如图,这次直接上实战。

 

使用d3-drag实现交互式拖拽功能

数据可视化中,交互是非常重要的一部分,能够使用户与图表进行互动并进行自定义操作。其中之一就是拖拽功能,可以让用户自由地移动元素,改变元素的位置或顺序。在D3.js中,我们可以使用d3-drag来实现这样的交互式拖拽功能。

1. 引入d3-drag库

首先,我们需要在HTML文件中引入d3.js和d3-drag库。可以通过CDN或本地文件的方式引入,如下所示:

2. 创建可拖拽的元素

接下来,我们需要创建一个可拖拽的元素。在这个示例中,我们将创建一个简单的矩形,并添加拖拽功能。首先,在HTML中创建一个SVG容器:

html复制代码<svg id="container" width="400" height="300"></svg>

然后,在JavaScript代码中使用D3.js创建一个矩形元素,并添加拖拽功能:

  1. javascript复制代码// 创建一个SVG容器
  2. const svg = d3.select("#container");
  3. // 创建矩形元素
  4. const rect = svg.append("rect")
  5. .attr("x", 100)
  6. .attr("y", 100)
  7. .attr("width", 50)
  8. .attr("height", 50)
  9. .attr("fill", "blue");
  10. // 创建拖拽行为
  11. const drag = d3.drag()
  12. .on("start", dragStarted)
  13. .on("drag", dragged)
  14. .on("end", dragEnded);
  15. // 将拖拽行为应用到矩形元素上
  16. rect.call(drag);
  17. // 拖拽开始时的回调函数
  18. function dragStarted(event, d) {
  19. d3.select(this).raise().attr("fill", "red");
  20. }
  21. // 拖拽过程中的回调函数
  22. function dragged(event, d) {
  23. d3.select(this)
  24. .attr("x", event.x)
  25. .attr("y", event.y);
  26. }
  27. // 拖拽结束时的回调函数
  28. function dragEnded(event, d) {
  29. d3.select(this).attr("fill", "blue");
  30. }

在上面的代码中,我们首先使用d3.select()选择了SVG容器,并使用append()方法创建了一个矩形元素。然后,我们创建了一个拖拽行为,并将其应用到矩形元素上,通过调用rect.call(drag)。

在拖拽行为的回调函数中,我们可以定义拖拽开始、拖拽过程和拖拽结束时的操作。在拖拽开始时,我们将选中的元素提到最前面,并改变其填充颜色。在拖拽过程中,我们使用event.x和event.y来更新元素的位置。在拖拽结束时,我们将填充颜色恢复为初始值。


我们已经掌握了drag的基本用法,下面我们来开发一个颜色匹配的小游戏吧! 游戏思路

  1. 预备5个颜色和对应的中文名称
  2. 用黑色绘制5个矩形区域,填充颜色的名称。
  3. 绘制出5个色块和5个名称对应上。
  4. 移动色块靠近背景区域的时候,当颜色匹配上的时候会出现提示(颜色边框发生变色),释放鼠标会自动吸附到对应色块的中心位置。
  5. 当所有色块都移动到正确位置的时候,提示完成所有的颜色匹配。

这是一个有意思的小游戏,适合小朋友玩哦。改造一个可以开发出各种类似的游戏,比如动物认识。人物认识,形状认识。呵呵

代码如下,自己看,懒得解释了。

  1. javascript复制代码// drag
  2. let dom = document.createElement('div');
  3. document.body.appendChild(dom);
  4. let padding = 30;
  5. let svgWidth = 600;
  6. let svgHeight = 300;
  7. let svg = d3.select(dom)
  8. .append("svg")
  9. .attr("width", svgWidth)
  10. .attr("height", svgHeight)
  11. .style('border', '1px solid #999999')
  12. // 定义5个颜色
  13. let colors = [
  14. {
  15. id: 0,
  16. name: '橙色',
  17. color: '#ff6633'
  18. },
  19. {
  20. id: 1,
  21. name: '蓝色',
  22. color: '#3399ff'
  23. },
  24. {
  25. id: 2,
  26. name: '绿色',
  27. color: '#33cc33'
  28. },
  29. {
  30. id: 3,
  31. name: '紫色',
  32. color: '#cc33ff'
  33. },
  34. {
  35. id: 4,
  36. name: '黄色',
  37. color: '#ffcc00'
  38. }
  39. ]
  40. // 绘制5个矩形
  41. let rectWidth = 100;
  42. let rectHeight = 100;
  43. let rectPadding = 10;
  44. let pos = {}
  45. // 绘制5个矩形背景边框
  46. let gback = svg.append('g').attr('class', 'back');
  47. let rectBacks = gback.selectAll('rect')
  48. .data(colors)
  49. .enter()
  50. .append('rect')
  51. .attr('x', (d, i) => {
  52. let x = padding + (rectWidth + rectPadding) * i;
  53. pos[d.id] = [x, padding]
  54. return x;
  55. })
  56. .attr('cid', d => d.name)
  57. .attr('ok', 'fail')
  58. .attr('y', padding)
  59. .attr('width', rectWidth)
  60. .attr('height', rectHeight)
  61. .attr('fill', 'none')
  62. .attr('stroke', '#000000')
  63. .attr('stroke-width', '2px')
  64. let _colors = JSON.parse(JSON.stringify(colors));
  65. // 打乱顺序
  66. _colors.sort(function () {
  67. return Math.random() - 0.5;
  68. })
  69. let grect = svg.append('g').attr('class', 'rect');
  70. let rects = grect.selectAll('rect')
  71. .data(_colors)
  72. .enter()
  73. .append('rect')
  74. .attr('cid', d => d.name)
  75. .attr('x', (d, i) => {
  76. return padding + (rectWidth + rectPadding) * i + rectWidth * 0.1;
  77. })
  78. .attr('y', (d, i) => {
  79. return padding + rectHeight + 50;
  80. })
  81. .attr('width', rectWidth * 0.8)
  82. .attr('height', rectHeight * 0.8)
  83. .attr('fill', d => d.color)
  84. // 在背景矩形上添加文字 颜色名称
  85. let gtext = svg.append('g').attr('class', 'text');
  86. let texts = gtext.selectAll('text')
  87. .data(colors)
  88. .enter()
  89. .append('text')
  90. .attr('x', (d, i) => {
  91. return padding + (rectWidth + rectPadding) * i + rectWidth / 2;
  92. })
  93. .attr('y', (d, i) => {
  94. return padding + rectHeight / 2;
  95. })
  96. .attr('text-anchor', 'middle')
  97. .attr('dominant-baseline', 'middle')
  98. .attr('fill', '#000000')
  99. .attr('font-size', '20px')
  100. .text(d => d.name)
  101. .style('pointer-events', 'none');
  102. // 提示文本 干得漂亮,你完成了所有的颜色匹配
  103. let infoText = svg
  104. .append('text')
  105. .attr('x', svgWidth / 2)
  106. .attr('y', svgHeight - 80)
  107. .attr('text-anchor', 'middle')
  108. .attr('dominant-baseline', 'middle')
  109. .attr('fill', '#000000')
  110. .attr('font-size', '24px')
  111. .text('干得漂亮Tom,你完成了所有的颜色匹配!')
  112. .style('pointer-events', 'none')
  113. .style('opacity', 0);
  114. let drag = d3.drag()
  115. .on('start', function (d, i) {
  116. d3.select(this)
  117. .raise()// 提升层级 使得当前元素在最上层
  118. .transition()
  119. .attr('stroke', '#000000')
  120. .attr('stroke-width', '2px')
  121. // 鼠标样式
  122. .style('cursor', 'move')
  123. })
  124. .on('drag', function (d) {
  125. let dx = d3.event.dx;
  126. let dy = d3.event.dy;
  127. let x = d3.select(this).attr('x');
  128. let y = d3.select(this).attr('y');
  129. d3.select(this)
  130. .attr('x', +x + dx)
  131. .attr('y', +y + dy);
  132. // 矩形中心点坐标
  133. let cx = +x + dx + (rectWidth * 0.8) / 2;
  134. let cy = +y + dy + (rectHeight * 0.8) / 2;
  135. let cid = d3.select(this).attr('cid');
  136. dis(d3.select(this), cx, cy, cid);
  137. })
  138. .on('end', function (d, i) {
  139. d3.select(this)
  140. .transition()
  141. .attr('stroke', 'none')
  142. //鼠标样式
  143. .style('cursor', 'default')
  144. if (d3.select(this).attr('ok') === 'success') {
  145. // 吸附到背景矩形上
  146. let _x = pos[d.id][0] + rectWidth / 2 - rectWidth * 0.8 / 2;
  147. let _y = pos[d.id][1] + rectHeight / 2 - rectHeight * 0.8 / 2;
  148. d3.select(this)
  149. .transition()
  150. .attr('x', _x)
  151. .attr('y', _y)
  152. }
  153. goodJob();
  154. })
  155. rects.call(drag);
  156. function dis(rect, x, y, cid) {
  157. // 计算x y和 rectBacks 中心点的距离 如果小于 30 就 变色
  158. rectBacks.each(function (d, i) {
  159. let cx = pos[i][0] + rectWidth / 2;
  160. let cy = pos[i][1] + rectHeight / 2;
  161. let _cid = d3.select(this).attr('cid');
  162. let distance = Math.sqrt(Math.pow(cx - x, 2) + Math.pow(cy - y, 2));
  163. if (distance < 30 && cid == _cid) {
  164. d3.select(this)
  165. .attr('stroke', d.color)
  166. .attr('ok', 'success')
  167. rect.attr('ok', 'success')
  168. } else {
  169. if (cid != _cid) return;
  170. d3.select(this)
  171. .attr('stroke', '#000000')
  172. .attr('ok', 'fail')
  173. rect.attr('ok', 'fail')
  174. }
  175. if (d3.select(this).attr('ok') != 'success') {
  176. gogogo = false;
  177. }
  178. })
  179. }
  180. function goodJob() {
  181. let gogogo = true;
  182. rectBacks.each(function (d, i) {
  183. if (d3.select(this).attr('ok') === 'fail') {
  184. gogogo = false;
  185. }
  186. })
  187. if (gogogo) {
  188. infoText
  189. .transition()
  190. .style('opacity', 1)
  191. } else {
  192. infoText
  193. .transition()
  194. .style('opacity', 0)
  195. }
  196. }

在线地址:scqilin.github.io/d3js/intera…

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/529208
推荐阅读
相关标签
  

闽ICP备14008679号