Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   2d карусель, работает, но уверен - неоптимально. (https://javascript.ru/forum/project/48447-2d-karusel-rabotaet-no-uveren-neoptimalno.html)

alko 04.07.2014 14:03

2d карусель, работает, но уверен - неоптимально.
 
Недавно начал изучать js. Сталкивался с различными каруселями, путался в коде, когда хотел переделать под свои нужды. Пришел к тому, что на jquery быстрей свою состряпать. Но захотелось изучить как же это делается без сторонних фреймворков и плагинов. Почитал статью JS-Анимация (http://learn.javascript.ru/js-animation) и воспользовавшись предоставленным кодом, написал вот такую карусельку http://learn.javascript.ru/play/wnCIjb . Знаю, что сделал неоптимально и неуниверсально. Еще и запутался, когда оптимизировал, приделал кучу ненужных переменных, часть уже убрал. Буду рад, если наставите на путь истинный)) . Сейчас изучаю js дальше, хочу сделать, чтобы эта карусель анимировалась по нажатию и движению мышью. На эту тему лучше не подсказывайте, сделаю, дам ссылку и выслушаю критику.
Даю ссылку http://learn.javascript.ru/play/sKS3nc . Сделал, работает местами корявенько.

На данный момент сделал так http://learn.javascript.ru/play/orjntb . Можно задавать количество картинок по горизонтали и вертикали, но в IE не работает автоматическое добавление недостающих картинок.

Теперь и в ie работает. http://learn.javascript.ru/play/MWYWTb
Добавил комментарии, старался поподробней (может кому приготится). http://learn.javascript.ru/play/nKxGNb

Sweet 04.07.2014 14:38

alko, совет: не перебирай массивы циклом for..in. Во-первых, туда может прилететь что-нибудь из прототипа. Если, например, для кроссбраузерности добавить Array.prototype.forEach туда, где таких методов нет. Во-вторых, в массиве могут быть не только числовые ключи. Например, RegExp.prototype.exec возвращает массив Array, но в нём есть свойства index и
input. И это нормально.
Я это к тому, что есть нормальные способы:
var idblock = ['lt','ct','rt','lm','cm','rm','lb','cb','rb'];
var button = idblock.map(function (id) {
    return document.getElementById(id);
});

alko 04.07.2014 15:00

Спасибо. Не знал, что можно так использовать map, вообще в синтаксисе еще плаваю. http://learn.javascript.ru/play/unV86

рони 04.07.2014 15:55

alko,
на время одной анимации неплохобы незапускать другие либо заканчивать предыдущую - в jquery stop

alko 04.07.2014 16:16

jquery в этой карусели не использую. Но об этом косяке знаю, на очереди. Вся конструкция рушится, если часто кликать по кнопкам.

рони 04.07.2014 16:29

Цитата:

Сообщение от alko
jquery в этой карусели не использую

и кто говорит об использовании ? я о функции stop как методе -- который -- останавливает анимацию и может переместить анимируемое по желанию в параметры в которые обьект анимировался.

рони 04.07.2014 16:39

что то типа var progress = stop || (new Date - start) / opts.duration;

stop = 0 нормальный ход.
stop = 2 экстренное завершение

alko 06.07.2014 13:37

Спасибо за совет, не хочу делать экстренное завершение анимации, наверное это будет не красиво, но переменную stop ввел http://learn.javascript.ru/play/TUvNsc .

рони 06.07.2014 20:00

Собери картинку, игрушка.
 
alko,
зарисовка на тему ... только перемещаются не блоки а background ... похоже но не совсем.
<!DOCTYPE HTML>
<html>
<head>
  <title></title>
<style type="text/css">td.mouse{
  height:98px;
  width:98px;
  text-align:center;
  margin:0px;
  background:url(http://dl9.glitter-graphics.net/pub/1432/1432279yonsqtq9b6.jpg) repeat;
  border-radius:0px;
}

table{
  border-collapse:collapse;
}

td.circle:after{
  content:'';
  display:block;
  width:20px;
  height:20px;
  background:#F00;
  border-radius:10px;
  margin:25% auto;
}

td.circle{
  height:20px;
  width:20px;
  text-align:center;
}

td{
  transition:all 1s;
  -moz-transition:all 1s;
  -webkit-transition:all 1s;
  -o-transition:all 1s;
}
</style>
</head>
<body>
<script language="JavaScript" type="text/javascript">
for (var l = 5, k, t = document.createElement("table"), c = 0; c < l; c++)
    for (var tr = t.insertRow(c), s = 0; s < l; s++) {
        k = 1;
        if (0 == c || c == l - 1 || 0 == s || s == l - 1) k = 0;
        var td = tr.insertCell(s),
            n = 50 * (s - 1) + "% " + 50 * (c - 1) + "%";
        k || s == c || 4 == s + c || (td.className = "circle", td.onclick = function (b, h) {
            return function () {
                for (var f, m = b && h ? -50 : 50,
                q = document.querySelectorAll("td"),
                g = b && 4 != b ? (f = b - 1, [6, 11, 16]) : (f = 5 * (h - 1), [6, 7, 8]), a = 0; a < g.length; a++) {
                    var p = q[g[a] + f],
                        d = getPosition(p);
                     p.style.backgroundPosition = b && 4 != b ? d[0] + "% " + (d[1] + m) + "%" : d[0] + m + "% " + d[1] + "%"
                }

            }
        }(s, c));
        k && (td.className = "mouse", td.style.backgroundPosition = n)
    }
t.cellspacing = "0";
t.cellpadding = "0";
document.body.appendChild(t);

function getPosition(a) {
    a = (window.getComputedStyle ? getComputedStyle(a, null) : a.currentStyle).backgroundPosition;
    a = a.match(/-?\d+(\.\d+)?/g) || [0, 0];
    a[0] = 50 * Math.round(a[0] / 50);
    a[1] = 50 * Math.round(a[1] / 50);
    return a
};

function go(a) {
    var b = document.querySelectorAll("td.circle");
    a--;
    var c = Math.floor(Math.random() * b.length);
    b[c].onclick();
    a && window.setTimeout(function () {
        go(a)
    }, 1500)
};
</script>
<input type="button" name="" value="go"  onclick="go(5)"/>


</body>
</html>

alko 07.07.2014 11:41

рони, очень интересно, коротко и эффективно. Дня два у меня наверное уйдет, чтоб разобраться в логике зарисовки.. Тоже думал сделать перемещение фонов, потому как, если инициировать анимацию "щипком" мыши, то цепляешь изображение вставленное в блок (что не красиво).
И сделал http://learn.javascript.ru/play/56G7kb

alko 08.07.2014 14:02

Доделал http://learn.javascript.ru/play/sKS3nc .

рони 08.07.2014 14:22

alko,
:)

alko 10.07.2014 11:52

Сделал универсальный вариант. Можно задавать размеры картинок и количество по горизонтали и вертикали. Если картинок не хватает, дополняются автоматически. Вот только при жестах мыши периодически выскакивает ошибка:
TypeError: blockmove[(colmove - 1)] is undefined
newblock.style.background = blockmove[colmove -1].style.background;
index.html (строка 224).
При нажатии кнопок и автоматической анимации в начале, ее не возникает. Не пойму почему.. http://learn.javascript.ru/play/lZ9CSb

рони 10.07.2014 12:36

alko,
строка 93 фигурные скобки пропущены

alko 10.07.2014 13:27

рони, не вижу пропущенных скобок, где они должны быть? По поводу своей ошибки, тупо исключил ее, т.к. понять не могу http://learn.javascript.ru/play/xalZ3b . И заметил, что IE при недостающем количестве картинок и автоматическом их добавлении, не устанавливает стили части блоков.

рони 10.07.2014 13:48

Цитата:

Сообщение от alko
рони, не вижу пропущенных скобок, где они должны быть?

function ()
{

}

alko 10.07.2014 14:20

Может у меня галлюцинации.. строка 93? точно?
button.forEach(function(but, k) {
but.onclick = function() {
kuda(k);
}
})

рони 10.07.2014 14:30

alko,
http://learn.javascript.ru/play/k612cb строка 93

alko 11.07.2014 11:30

рони, спасибо, уже исправил. Там оно не нужно вообще. Вот что делать с ie? При присвоении бэкграунда новому блоку в 48 строке http://learn.javascript.ru/play/RLGdrb , в ie происходит удаление из массива блока бэкграунд которого присваивается (причем поэкспериментировав выяснил, что присваивается и содержимое блока удаляясь из исходного). И в итоге ему не назначаются стили высоты, ширины и топ, лефт, то есть он не отображается в итоге. Почему так происходит и как это исправить? В остальном вроде все работает.

Я не прав, удаление из массива не происходит, но эти элементы все равно не обрабатываются как в др. браузерах. И ie 8 строит как надо, а более поздние версии этого не делают.

рони 11.07.2014 12:04

alko,
newblock.style.background = cont[dop].style.background; попробуйте перечислять что необходимо backgroundСolor и т.д.

alko 11.07.2014 12:20

рони, пробовал присваивать только backgroundImage, результат тот же.

alko 11.07.2014 12:31

даже если везде поменять бэкграунд на бэкграунд имидж:
<!DOCTYPE html>
<html lang="en">
    <head>
		<meta charset="UTF-8" /> 
        <title>2dcarusel</title>
        <style type="text/css">
			html, body{
				width:100%;
			}
			html, body, div{
				margin:0;
				padding:0;
				border:0;
			}
			#contcar{
				position:relative;
				top:50px;
				margin:0 auto;
			}
			#container{
				position:relative;
				overflow:hidden;
			}
			#container div, #contcar div{
				display:block;
				position:absolute;
			}
		</style>
		<script type="text/javascript" language="JavaScript">
			window.onload = function(){
				var width = 150;               //ширина одного изображения
				var height = 100;              //высота одного изображения
				var colx = 3;                  //количество изображений по горизонтали
				var coly = 4;                  //количество изображений по вертикали
				var but_size = 15;             //размер кнопки по горизонтали (если справа или слева) или вертикали (если сверху или снизу)
				var butbackground_ud = 'url(ud.jpg) no-repeat 0 0';     //изображение кнопки сверху или снизу от контейнера
				var butbackground_lr = 'url(lr.jpg) no-repeat 0 0';     //изображение кнопки слева или справа от контейнера
				var container = document.getElementById('container');
				container.style.width = width * colx + 'px';
				container.style.height = height * coly + 'px';
				document.getElementById('contcar').style.width = width * colx + 'px';
				document.getElementById('contcar').style.height = height * coly + 'px';
				var cont = container.getElementsByTagName('div');
				/* for (var i = 0; i < cont.length; i++) cont[i].innerHTML = i; */
				var dop = 0;
				while (cont.length < colx * coly) {
					cont[cont.length] = cont[dop];
					var dopblock = document.createElement('div');
					dopblock.style.backgroundImage = cont[dop].style.backgroundImage;
					container.appendChild(dopblock);
					dop++;
				}
				cont = container.getElementsByTagName('div');
				var block = [];
				for (var i = 0; i < colx * coly; i++) block[i] = cont[i];
				var idblock = [];
				for (var i = 0; i < coly; i++){
					for (var j = 0; j < colx; j++){
						var bl = block[i * colx + j];
						bl.style.width = width + 'px';
						bl.style.height = height + 'px';
						bl.style.left = width * j + 'px';
						bl.style.top = height * i + 'px';
						bl.id = 'b' + (i * colx + j);
						idblock[i * colx + j] = bl.id;
						/* console.log(bl.id); */
					}
				}
				var button = [];
				for (var i = 0; i < 2 * (colx + coly); i++) {
					var but = document.createElement('DIV');
					if (i < colx * 2) {
						but.style.background = butbackground_ud;
						but.style.width = width + 'px';
						but.style.height = but_size + 'px';
						but.style.left = ((i < colx) ? i * width : (i - colx) * width) + 'px';
						but.style.top = ((i < colx) ? - but_size : coly * height) + 'px';
					}
					else {
						but.style.background = butbackground_lr;
						but.style.width = but_size + 'px';
						but.style.height = height + 'px';
						but.style.left = ((i > 2 * colx + coly - 1) ? colx * width : - but_size) + 'px';
						but.style.top = ((i > 2 * colx + coly - 1) ? (i - 2 * colx - coly) * height : (i - 2 * colx) * height) + 'px';
					}
					but.style.cursor = 'pointer';
					but.id = 'but' + i;
					document.getElementById('contcar').appendChild(but);
					button[i] = document.getElementById('but' + i);
				}
				var stop = 0;
				var z = setInterval(function() { 
					kuda (Math.floor(Math.random() * (2 * (colx + coly))));
				}, 1000);
				button.forEach(function(but, k) {
					but.onclick = function() {
						kuda(k);
					}
				})
				var x, y;
				var curblock;
				window.onmousedown = startmove;
				function startmove(e) {
					clearInterval(z);
					x = e.pageX;
					y = e.pageY;
					curblock = whichblock();
					window.onmousemove = wichmove;
				}
				function wichmove(e) {
					if (curblock != null) {
						setTimeout(function() {
							var x1 = e.pageX - x;
							var y1 = e.pageY - y;
							var r;
							if (Math.abs(x1) > Math.abs(y1)) {
								if (x1 > 0) {
									r = 2 * colx + coly + Math.floor(curblock / colx);
								}
								else if (x1 < 0) {
									r = 2 * colx + Math.floor(curblock / colx);
								}
							}
							else if (Math.abs(x1) < Math.abs(y1)) {
								if (y1 < 0) {
									r = curblock % colx;
								}
								else if (y1 > 0) {
									r = colx + curblock % colx;
								}
							}
							kuda(r);
							x1 = 0;
							y1 = 0;
							window.onmousemove = false;
						}, 50);
					}
					return false;
				}
				function whichblock() {
					var cur = null;
					block = idblock.map(function (id) {
						return document.getElementById(id);
					});
					for (var i = 0; i < block.length; i++) {
						if (
						(block[i].offsetLeft < x - document.getElementById('contcar').offsetLeft) && 
						(block[i].offsetLeft + block[i].offsetWidth > x - document.getElementById('contcar').offsetLeft) &&
						(block[i].offsetTop < y - document.getElementById('contcar').offsetTop) && 
						(block[i].offsetTop + block[i].offsetHeight > y - document.getElementById('contcar').offsetTop)
						) {
							cur = i;
							break;
						}
					}
					return cur;
				}
				function kuda(k) {
					if (stop == 0) {
						stop = 1;
						block = idblock.map(function (id) {
							return document.getElementById(id);
						});
						var a;
						var b;
						var size1;
						var size2;
						var prop1;
						var prop2;
						var step;
						var m;
						var colmove;
						var blockmove = [];
						var current = 0;
						if (k < colx * 2) {
							size1 = height;
							size2 = width;
							prop1 = 'top';
							prop2 = 'left';
							colmove = coly;
							if (k < colx) {
								step = -1;
								m = k;
							}
							else {
								step = 1;
								m = k - colx;
							}
						}
						else {
							size1 = width;
							size2 = height;
							prop1 = 'left';
							prop2 = 'top';
							colmove = colx;
							if (k < 2 * colx + coly) {
								step = -1;
								m = k - 2 * colx;
							}
							else {
								step = 1;
								m = k - 2 * colx - coly;
							}
						}
						for (var i = 0; i < coly; i++){
							for (var j = 0; j < colx; j++){
								var bl = block[i * colx + j];
								if (k < colx * 2) {
									if (m == j) {
										blockmove[current] = bl;
										current++;
									}
								}
								else {
									if (m == i) {
										blockmove[current] = bl;
										current++;
									}
								}
							}
						}
						var newblock = document.createElement('div');
						newblock.style.width = width + 'px';
						newblock.style.height = height + 'px';
						if (blockmove[blockmove.length - 1] != null) {
							if (step == 1) {
								newblock.style.backgroundImage = blockmove[blockmove.length - 1].style.backgroundImage;
								newblock.style[prop1] = - size1 + 'px';
								blockmove.unshift(newblock);
								a = 1;
							}
							else {
								newblock.style.backgroundImage = blockmove[0].style.backgroundImage;
								newblock.style[prop1] = colmove * size1 + 'px';
								blockmove.push(newblock);
								a = 0;
							}
							newblock.style[prop2] = m * size2 + 'px';
							container.appendChild(newblock);
							for (var i = 0; i < blockmove.length; i++) {
								move(blockmove[i], prop1, (i - a) * size1, (i - a + step) * size1);
							}
							setTimeout(function() {
								if (step == 1) {
									for (var i = 0; i < colmove; i++) blockmove[i].id = blockmove[i + 1].id;
									container.removeChild(blockmove[colmove]);
								}
								else{
									for (var i = colmove; i > 0; i--) blockmove[i].id = blockmove[i - 1].id;
									container.removeChild(blockmove[0]);
								}
								stop = 0;
							}, 350);
						}
						else {
							stop = 0;
						}
					}
				}
			};
			function move(elem, prop, start, end) {
				animateProp({
					delay: 10,
					duration: 300,
					delta: makeEaseInOut(quad),
					start: start,
					end: end,
					prop: prop,
					elem: elem
				});
			}
			function animate(opts) {
			  var start = new Date;
			  var delta = opts.delta || linear;
			  var timer = setInterval(function() {
				var progress = (new Date - start) / opts.duration;
				if (progress > 1) progress = 1;
				opts.step( delta(progress) );
				if (progress == 1) {
				  clearInterval(timer);
				  opts.complete && opts.complete();
				}
			  }, opts.delay || 13);
			  return timer;
			}
			function animateProp(opts) {
			  var start = opts.start, end = opts.end, prop = opts.prop;
			  opts.step = function(delta) {
				var value = Math.round(start + (end - start)*delta);
				opts.elem.style[prop] = value + 'px';
			  }
			  return animate(opts);
			}
			function quad(progress) {
			  return Math.pow(progress, 2);
			}
			function makeEaseInOut(delta) {
			  return function(progress) {
				if (progress < .5)
				  return delta(2*progress) / 2;
				else
				  return (2 - delta(2*(1-progress))) / 2;
			  }
			}
		</script>
    </head>
    <body>
		<div id="contcar">
			<div id="container">
				<div style="background-image:url(1.jpg);"></div>
				<div style="background-image:url(2.jpg);"></div>
				<div style="background-image:url(3.jpg);"></div>
				<div style="background-image:url(4.jpg);"></div>
				<div style="background-image:url(5.jpg);"></div>
				<div style="background-image:url(6.jpg);"></div>
				<div style="background-image:url(7.jpg);"></div>
				<div style="background-image:url(8.jpg);"></div>
				<div style="background-image:url(9.jpg);"></div>
			</div>
        </div>
    </body>
</html>

alko 21.07.2014 14:59

Нашел ошибку, убрал строку 47, если посмотреть в предыдущий пост. Теперь в ie выше 8й версии работает нормально. Сейчас добавлю в шапку окончательный вариант. Окончательный, т.к. далее вылизывать код смысла не вижу, делал это с целью обучения.


Часовой пояс GMT +3, время: 02:31.