jQueryで画像をランダムにばら撒いたように配置する
以前、飲食店サイトを制作した際に、記念写真を掲載する目的でメインビューに使っていたスクリプトを思い出しのたのでメモ。卓上に写真を広げるような感じでレイアウトでき、ドラッグ操作で動かせるので、一覧表示と比べると若干の見づらさはあるものの、目的のものを探し出す体験も含めて、一定の効果はありそうです。 HTML <ul id="photo-list"> <li><a href=""><img src="○○.jpg" alt="○○"></a></li> <li><a href=""><img src="○○.jpg" alt="○○"></a></li> <li><a href=""><img src="○○.jpg" alt="○○"></a></li> <li><a href=""><img src="○○.jpg" alt="○○"></a></li> <li><a href=""><img src="○○.jpg" alt="○○"></a></li> <li><a href=""><img src="○○.jpg" alt="○○"></a></li> <li><a href=""><img src="○○.jpg" alt="○○"></a></li> <li><a href=""><img src="○○.jpg" alt="○○"></a></li> <li><a href=""><img src="○○.jpg" alt="○○"></a></li> <li><a href=""><img src="○○.jpg" alt="○○"></a></li> </ul> CSS #photo-list { position: relative; width: 100%; height: 100vh; margin: 0 auto; padding: 0; background: #f5f5f5; overflow: hidden; } #photo-list li { list-style: none; position: absolute; width: 25vw; /* 親幅に応じて自動調整 */ left: 100vw; } #photo-list li:active { cursor: grabbing; } #photo-list li img { width: 100%; display: block; box-shadow: 0 4px 10px rgba(0,0,0,0.3); border-radius: 6px; transition: transform 0.8s ease-out; user-select: none; } jQuery var $items = $('#photo-list li'); var $container = $('#photo-list'); var zIndexCounter = 1; function getAreaSize() { return { width: $container.width(), height: $container.height() }; } function getItemSize($el) { return { width: $el.width(), height: $el.height() }; } // 初期位置:右外からばらまく $items.each(function() { var area = getAreaSize(); var item = getItemSize($(this)); $(this).css({ left: area.width + 200, top: area.height / 2 - item.height / 2, opacity: 0, transform: 'rotate(0deg) scale(0.8)' }); }); // ばらまきアニメーション setTimeout(() => { $items.each(function() { var area = getAreaSize(); var item = getItemSize($(this)); var left = Math.random() * (area.width - item.width); var top = Math.random() * (area.height - item.height); var rotate = (Math.random() * 60) - 30; var delay = Math.random() * 800; $(this).delay(delay).animate({ left: left, top: top, opacity: 1 }, { duration: 1000, easing: 'swing', step: function(_, fx) { $(this).css('transform', `rotate(${rotate}deg) scale(1)`); } }); }); }, 500); if ($(window).width() < 980) { // スマホ・タブレット用ドラッグ $items.on({ touchstart: function(e) { var $this = $(this); var touch = e.originalEvent.touches[0]; $this.data({ startX: touch.pageX, startY: touch.pageY, offsetX: touch.pageX - $this.position().left - $container.offset().left, offsetY: touch.pageY - $this.position().top - $container.offset().top, isDragging: false, dragOccurred: false }); zIndexCounter++; $this.css('z-index', zIndexCounter); }, touchmove: function(e) { var $this = $(this); var touch = e.originalEvent.touches[0]; var startX = $this.data('startX'); var startY = $this.data('startY'); var offsetX = $this.data('offsetX'); var offsetY = $this.data('offsetY'); var isDragging = $this.data('isDragging'); var dragOccurred = $this.data('dragOccurred'); var dragThreshold = 5; var deltaX = Math.abs(touch.pageX - startX); var deltaY = Math.abs(touch.pageY - startY); if (!isDragging && (deltaX > dragThreshold || deltaY > dragThreshold)) { isDragging = true; dragOccurred = true; } if (isDragging) { e.preventDefault(); $this.css({ left: touch.pageX - $container.offset().left - offsetX + 'px', top: touch.pageY - $container.offset().top - offsetY + 'px' }); } $this.data({ isDragging: isDragging, dragOccurred: dragOccurred }); }, touchend: function(e) { var $this = $(this); var dragOccurred = $this.data('dragOccurred'); if (dragOccurred) e.preventDefault(); $this.removeData('isDragging dragOccurred startX startY offsetX offsetY'); } }); } else { // PC用ドラッグ $items.on('mousedown', function(e) { var $this = $(this); e.preventDefault(); var startX = e.pageX; var startY = e.pageY; var offsetX = startX - $this.position().left - $container.offset().left; var offsetY = startY - $this.position().top - $container.offset().top; var isDragging = false; var dragThreshold = 3; var dragOccurred = false; zIndexCounter++; $this.css('z-index', zIndexCounter); function clickPreventer(e) { e.preventDefault(); } function mouseMoveHandler(e) { var deltaX = Math.abs(e.pageX - startX); var deltaY = Math.abs(e.pageY - startY); if (!isDragging && (deltaX > dragThreshold || deltaY > dragThreshold)) { isDragging = true; dragOccurred = true; $this.find('a').on('click.prevent', clickPreventer); } if (isDragging) { $this.css({ left: e.pageX - $container.offset().left - offsetX + 'px', top: e.pageY - $container.offset().top - offsetY + 'px' }); } } function mouseUpHandler(e) { $(document).off('mousemove', mouseMoveHandler); $(document).off('mouseup', mouseUpHandler); if (dragOccurred) { $this.one('click', function(e) { e.preventDefault(); }); $this.find('a').off('click.prevent', clickPreventer); } } $(document).on('mousemove', mouseMoveHandler); $(document).on('mouseup', mouseUpHandler); }); } SAMPLE #photo-list { position: relative; width: 100%; height: 100vh; /* 画面幅に応じた高さ */ margin: 0 auto; padding: 0; background: #f5f5f5; overflow: hidden; } #photo-list li { list-style: none; position: absolute; width: 25vw; /* 親幅に応じて自動調整 */ left: 100vw; } #photo-list li:active { cursor: grabbing; } #photo-list li img { width: 100%; display: block; box-shadow: 0 4px 10px rgba(0,0,0,0.3); border-radius: 6px; transition: transform 0.8s ease-out; user-select: none; }




