jjzjj

javascript - 为什么有些细胞不能完全移动

coder 2025-01-07 原文

我已经设置了这个 jsfiddle:http://jsfiddle.net/386er/dhzq6q6f/14/

var moveCell = function(direction) {

var cellToBeMoved = pickRandomCell();
var currentX = cellToBeMoved.x.baseVal.value; 
var currentY = cellToBeMoved.y.baseVal.value;
var change = getPlusOrMinus() * (cellSize + 1 );
var newX = currentX + change;
var newY = currentY + change;
var selectedCell = d3.select(cellToBeMoved);


if (direction === 'x') {
    selectedCell.transition().duration(1500)
        .attr('x', newX );
} else {
    selectedCell.transition().duration(1500)
        .attr('y', newY );

}

在 moveCell 函数中,我选择了一个随机单元格,请求其当前的 x 和 y 坐标,然后加上或减去它的宽度或高度,以将其移动到相邻的单元格。

我想知道的是:如果您观察细胞移动,有些细胞只会部分移动到下一个细胞。谁能告诉我,为什么会这样?

最佳答案

在这种情况下要做的第一件事是将 .each("interrupt", function() { console.log("interrupted!") }); 放在你的转换上。然后你就会看到问题了。
如果您将转换命名为 selection.transition("name"),它应该会修复它,但这并不能修复它。
这意味着您必须按照@jcuenod 的建议进行操作,并排除正在移动的那些。一种惯用的方法是这样的......

if (direction === 'x') {
    selectedCell.transition("x").duration(1500)
      .attr('x', newX)
      .each("start", function () { lock.call(this, "lockedX") })
      .each("end", function () { unlock.call(this, "lockedX") });
} else {
    selectedCell.transition("y").duration(1500)
      .attr('y', newY)
      .each("start", function () { lock.call(this, "lockedX") })
      .each("end", function () { unlock.call(this, "lockedX") });
}

function lock(lockClass) {
    var c = { cell: false }; c[lockClass] = true;
    d3.select(this).classed(c)
};
function unlock(lockClass) {
    var c = { cell: this.classList.length == 1 }; c[lockClass] = false;
    d3.select(this).classed(c);
};

Here是证明这个概念的 fiddle 。


纯正地道的 d3 版本

只是为了完整性,这里是 d3 的方式。
我试图让它尽可能地地道。要点是...

  1. 纯数据驱动
    数据已更新,可视化操作完全留给 d3 声明。
  2. 使用 d3 检测 svg 元素属性的变化并采取行动
    这是通过在 selection.data() 方法中使用复合 key 函数并利用更改节点(xyfill 属性与更新后的数据不匹配)被退出选择捕获。
  3. 将更改的元素拼接到数据数组中,以便 d3 可以检测更改
    由于对数据数组元素的引用 绑定(bind)到 DOM 元素,因此对数据的任何更改也将反射(reflect)在 selection.datum() 中。 d3 使用 key 函数将数据值与 datum 进行比较,以便将节点分类为更新、进入或退出。如果生成了一个,即数据/基准值的函数,则不会检测到数据的更改。通过拼接-ing 更改到数据数组中,selection.datum() 引用的值将不同于数据数组,因此数据更改将标记退出节点。< br/=""> 通过简单地操纵属性并将转换放在退出选择上而不是将其删除,它实际上变成了“已更改”的选择。
    这仅在数据值是对象时才有效。
  4. 并发转换
    命名转换用于确保 x 和 y 转换不会相互中断,但也有必要使用标记类属性来锁定转换元素。这是使用转换开始和结束事件完成的。
  5. 动画帧
    d3.timer 用于平滑动画和编码资源。在每个动画帧之前,d3Timer 回调以更新转换之前的数据。
  6. 使用 d3.scale.ordinal() 管理定位
    这很棒,因为它每次都有效,您甚至不必考虑它。

$(function () {
	var container,
		svg,
		gridHeight = 800,
		gridWidth = 1600,
		cellSize, cellPitch,
		cellsColumns = 100,
		cellsRows = 50,
		squares,

		container = d3.select('.svg-container'),
		svg = container.append('svg')
			.attr('width', gridWidth)
			.attr('height', gridHeight)
			.style({ 'background-color': 'black', opacity: 1 }),

		createRandomRGB = function () {
			var red = Math.floor((Math.random() * 256)).toString(),
					green = Math.floor((Math.random() * 256)).toString(),
					blue = Math.floor((Math.random() * 256)).toString(),
					rgb = 'rgb(' + red + ',' + green + ',' + blue + ')';
			return rgb;
		},

		createGrid = function (width, height) {

			var scaleHorizontal = d3.scale.ordinal()
						.domain(d3.range(cellsColumns))
						.rangeBands([0, width], 1 / 15),
					rangeHorizontal = scaleHorizontal.range(),

					scaleVertical = d3.scale.ordinal()
						.domain(d3.range(cellsRows))
						.rangeBands([0, height]),
					rangeVertical = scaleVertical.range(),

					squares = [];
			rangeHorizontal.forEach(function (dh, i) {
				rangeVertical.forEach(function (dv, j) {
					var indx;
					squares[indx = i + j * cellsColumns] = { x: dh, y: dv, c: createRandomRGB(), indx: indx }
				})
			});

			cellSize = scaleHorizontal.rangeBand();
			cellPitch = {
				x: rangeHorizontal[1] - rangeHorizontal[0],
				y: rangeVertical[1] - rangeVertical[0]
			}

			svg.selectAll("rect").data(squares, function (d, i) { return d.indx })
				.enter().append('rect')
				.attr('class', 'cell')
				.attr('width', cellSize)
				.attr('height', cellSize)
				.attr('x', function (d) { return d.x })
				.attr('y', function (d) { return d.y })
				.style('fill', function (d) { return d.c });

			return squares;
		},

		choseRandom = function (options) {
			options = options || [true, false];
			var max = options.length;
			return options[Math.floor(Math.random() * (max))];
		},

		pickRandomCell = function (cells) {

				var l = cells.size(),
						r = Math.floor(Math.random() * l);

				return l ? d3.select(cells[0][r]).datum().indx : -1;

		};

	function lock(lockClass) {
		var c = { cell: false }; c[lockClass] = true;
		d3.select(this).classed(c)
	};
	function unlock(lockClass) {
		var c = { cell: this.classList.length == 1 }; c[lockClass] = false;
		d3.select(this).classed(c);
	};

	function permutateColours() {
		var samples = Math.min(50, Math.max(~~(squares.length / 50),1)), s, ii = [], i, k = 0,
				cells = d3.selectAll('.cell');
		while (samples--) {
			do i = pickRandomCell(cells); while (ii.indexOf(i) > -1 && k++ < 5 && i > -1);
			if (k < 10 && i > -1) {
				ii.push(i);
				s = squares[i];
				squares.splice(i, 1, { x: s.x, y: s.y, c: createRandomRGB(), indx: s.indx });
			}
		}

	}

	function permutatePositions() {
		var samples = Math.min(20, Math.max(~~(squares.length / 100),1)), s, ss = [], d, m, p, k = 0,
				cells = d3.selectAll('.cell');
		while (samples--) {
			do s = pickRandomCell(cells); while (ss.indexOf(s) > -1 && k++ < 5 && s > -1);
			if (k < 10 && s > -1) {
				ss.push(s);
				d = squares[s];
				m = { x: d.x, y: d.y, c: d.c, indx: d.indx };
				m[p = choseRandom(["x", "y"])] = m[p] + choseRandom([-1, 1]) * cellPitch[p];

				squares.splice(s, 1, m);
			}
		}
	}

	function updateSquares() {
        //use a composite key function to transform the exit selection into
        // an attribute update selection
        //because it's the exit selection, d3 doesn't bind the new data
        // that's done manually with the .each 
		var changes = svg.selectAll("rect")
			.data(squares, function (d, i) { return d.indx + "_" + d.x + "_" + d.y + "_" + d.c; })
			.exit().each(function (d, i, j) { d3.select(this).datum(squares[i]) })

		changes.transition("x").duration(1500)
			.attr('x', function (d) { return d.x })
			.each("start", function () { lock.call(this, "lockedX") })
			.each("end", function () { unlock.call(this, "lockedX") })

		changes.transition("y").duration(1500)
			.attr('y', function (d) { return d.y })
			.each("start", function () { lock.call(this, "lockedY") })
			.each("end", function () { unlock.call(this, "lockedY") });

		changes.attr("stroke", "white")
			.style("stroke-opacity", 0.6)
			.transition("fill").duration(800)
				.style('fill', function (d, i) { return d.c })
				.style("stroke-opacity", 0)
				.each("start", function () { lock.call(this, "lockedFill") })
				.each("end", function () { unlock.call(this, "lockedFill") });
	}
	squares = createGrid(gridWidth, gridHeight);

	d3.timer(function tick() {
		permutateColours();
		permutatePositions();
		updateSquares();
	});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
  <div class="svg-container"></div>

NOTE: requires d3 version 3.5.5 for the position transitions to run.

编辑:修复了锁定和解锁的问题。标记数据可能比将类写入 DOM 更好,但无论如何……这种方式很有趣。

关于javascript - 为什么有些细胞不能完全移动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30322620/

有关javascript - 为什么有些细胞不能完全移动的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  6. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  7. ruby - 完全离线安装RVM - 2

    我打算为ruby​​脚本创建一个安装程序,但我希望能够确保机器安装了RVM。有没有一种方法可以完全离线安装RVM并且不引人注目(通过不引人注目,就像创建一个可以做所有事情的脚本而不是要求用户向他们的bash_profile或bashrc添加一些东西)我不是要脚本本身,只是一个关于如何走这条路的快速指针(如果可能的话)。我们还研究了这个很有帮助的问题:RVM-isthereawayforsimpleofflineinstall?但有点误导,因为答案只向我们展示了如何离线在RVM中安装ruby。我们需要能够离线安装RVM本身,并查看脚本https://raw.github.com/wayn

  8. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  9. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  10. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

随机推荐