网站搜索

javascript - 构建用于互动叙事的滚动器的最简方式是什么?


我正在尝试建立一个静态但响应式的叙事 HTML 网站。我的目标是实现类似这样的效果:

左侧的文本,右侧固定位置的 *.jpg 图片。当用户滚动左侧的文本时,右侧的图片保持固定位置,但在左侧滚动达到某个点时,会以短暂过渡更换为另一张 *.jpg 图片。

在这种情况下,‘响应式’意味着我对在较小屏幕上禁用粘性滚动完全满意,并且文本和图像块只是简单地恢复到它们的正常断点行为。

我已经研究了各种讲故事和滚动的 JavaScript 库(Scrollstory、Waypoints 和 InView),以简单、轻量且易于实现这一目标。到目前为止,它们似乎对于我想要做的事情完全是过度设计的。

几乎找不到的教程和代码片段让我感觉有点像这样:

我找不到任何真正的参考资料告诉我如何从小步骤开始着手构建一个滚动器。不管使用哪个库。

各个库的参考文件(具有可访问代码的)通常已经过时,并且侧重于与外部数据可视化框架一起使用,并且无论出于何种原因都需要加载多达四个其他外部脚本,仍然无法提供响应功能。

我尝试在这里重建这个展示示例:https://russellgoldenberg.github.io/scrollama/sticky-side/,因为它至少包含了我想要实现的一半功能,但我无法让它工作,而且我也不理解那里过度使用的 JavaScript:https://jsfiddle.net/gdkupcb6/2/

  <article>
<div class='step' data-step='1'>
  <p>STEP 1</p>
</div>
<div class='step' data-step='2'>
  <p>STEP 2</p>
</div>
<div class='step' data-step='3'>
  <p>STEP 3</p>
</div>
<div class='step' data-step='4'>
  <p>STEP 4</p>
</div>

在 HTML 方面,这段代码看起来足够简单,几乎所有库我都能理解,但当涉及到 JavaScript 部分时,我完全迷失了。我为什么在这里需要 D3?对于滚动时的一个简单变化,为什么需要这么多 JavaScript 功能?

我是否完全错误地认为,有一种极简的方法可以实现这种相当简单的粘性滚动,而不涉及多个不同的外部脚本和库?

有人能指引我一个好的方向,一步步地构建一个滚动器,让我能够理解每一部分代码,而不依赖复杂的外部库以及那些看起来没有真正开头和结尾的教程吗?

答案#1

为此你可以使用 JavaScript——你可以添加一个 window.addEventListener("wheel") 或 "scroll",然后监听当你的 "picture".offsetTop 达到合适的滚动位置时,将该元素的位置设置为 "fixed"。此外,你还可以选择添加一个 "慢动作进入" 的效果,或只是调整图片随页面滚动的方式。

你的 jsFiddle 对我不起作用,但我喜欢这个想法,所以我在 codepen 上自己写了大约 30 行代码 ---DEMO--- 来实现类似的功能,有点笨拙,但很乐意听取其他人的建议,不过它确实能工作。目前它会随着页面滚动图片,看起来像是文本块开始和结束时图片在变化(我用的是表情符号而不是图片,但原理是一样的)。

它应该能自动适应你的屏幕尺寸,同时还能正常工作,但尚未经过测试,所以.. :D

var container = document.getElementById("container");
var stickyImage = document.getElementById("stickyImage");
var tempY;
window.addEventListener("scroll", (e) => {
//watch how image moves with scrolleing
    if(window.pageYOffset >= container.offsetTop && window.pageYOffset <= container.offsetTop+container.offsetHeight  - window.innerHeight ){
        console.log("slinding");
        stickyImage.style.position = "fixed";
        stickyImage.style.top = "0vh";
        stickyImage.style.marginTop = "";
        tempY = window.pageYOffset;
    }else if(window.pageYOffset <= container.offsetTop){
        console.log("top sxcreen");
        stickyImage.style.position = "absolute";
        stickyImage.style.top = "";
        stickyImage.style.marginTop = "";       
    }else{
        console.log("stop");
        stickyImage.style.position = "absolute";
        stickyImage.style.marginTop = tempY + "px";         
    }
//change images while scroll
    if(window.pageYOffset < document.getElementById("change1").offsetTop){
        stickyImage.textContent = "🍓🍓🍓";
    }
    if(window.pageYOffset > document.getElementById("change1").offsetTop){
        stickyImage.textContent = "🍌🍌🍌";
    }
    if(window.pageYOffset > document.getElementById("change2").offsetTop){
        stickyImage.textContent = "🍏🍏🍏";
    }   
})

答案#2

首先,为了让你的代码工作,你需要用 main 标签将它包裹起来。

// using d3 for convenience
var main = d3.select('main')
var scrolly = main.select('#scrolly');
var figure = scrolly.select('figure');
var article = scrolly.select('article');
var step = article.selectAll('.step');

// initialize the scrollama
var scroller = scrollama();

// generic window resize listener event
function handleResize() {
  // 1. update height of step elements
  var stepH = Math.floor(window.innerHeight * 0.75);
  step.style('height', stepH + 'px');

  var figureHeight = window.innerHeight / 2
  var figureMarginTop = (window.innerHeight - figureHeight) / 2

  figure
    .style('height', figureHeight + 'px')
    .style('top', figureMarginTop + 'px');


  // 3. tell scrollama to update new element dimensions
  scroller.resize();
}

// scrollama event handlers
function handleStepEnter(response) {

  // response = { element, direction, index }

  // add color to current step only
  step.classed('is-active', function(d, i) {
    return i === response.index;
  })

  // update graphic based on step
  figure.select('p').text(response.index + 1);
}

function setupStickyfill() {
  d3.selectAll('.sticky').each(function() {
    Stickyfill.add(this);
  });
}

function init() {
  setupStickyfill();

  // 1. force a resize on load to ensure proper dimensions are sent to scrollama
  handleResize();

  // 2. setup the scroller passing options
  // 		this will also initialize trigger observations
  // 3. bind scrollama event handlers (this can be chained like below)
  scroller.setup({
      step: '#scrolly article .step',
      offset: 0.33,
      debug: true,
    })
    .onStepEnter(handleStepEnter)


  // setup resize event
  window.addEventListener('resize', handleResize);
}

// kick things off
init();
	#scrolly {
			position: relative;
			display: -webkit-box;
    	display: -ms-flexbox;
    	display: flex;
			background-color: #f3f3f3;
			padding: 1rem;
		}

		#scrolly > * {
			-webkit-box-flex: 1;
    	-ms-flex: 1;
    	flex: 1;
		}


		article {
			position: relative;
			padding: 0 1rem;
			max-width: 20rem;
		}

		figure {
			position: -webkit-sticky;
			position: sticky;
			width: 100%;
			margin: 0;
			-webkit-transform: translate3d(0, 0, 0);
			-moz-transform: translate3d(0, 0, 0);
			transform: translate3d(0, 0, 0);
			background-color: #8a8a8a;
		}

		figure p {
			text-align: center;
			padding: 1rem;
			position: absolute;
			top: 50%;
			left: 50%;
			-moz-transform: translate(-50%, -50%);
			-webkit-transform: translate(-50%, -50%);
			transform: translate(-50%, -50%);
			font-size: 8rem;
			font-weight: 900;
			color: #fff;
		}


		.step {
			margin: 0 auto 2rem auto;
			background-color: #3b3b3b;
			color: #fff;
		}

		.step:last-child {
			margin-bottom: 0;
		}

		.step.is-active {
			background-color: goldenrod;
			color: #3b3b3b;
		}

		.step p {
			text-align: center;
			padding: 1rem;
			font-size: 1.5rem;
		}
<main>

<section id='intro'>
  <h1 class='intro__hed'>Sticky Side Example</h1>
  <p class='intro__dek'>
    Start scrolling to see how it works.
  </p>
</section>

<section id='scrolly'>

  <article>
    <div class='step' data-step='1'>
      <p>STEP 1</p>
    </div>
    <div class='step' data-step='2'>
      <p>STEP 2</p>
    </div>
    <div class='step' data-step='3'>
      <p>STEP 3</p>
    </div>
    <div class='step' data-step='4'>
      <p>STEP 4</p>
    </div>
  </article>

  <figure>
    <p>0</p>
  </figure>
</section>
</main>

<script src="https://unpkg.com/[email /intersection-observer.js"></script>
<script src="https://unpkg.com/scrollama"></script>
<script src='https://unpkg.com/[email /dist/d3.min.js'></script>

其次,我看不出除了使用 JavaScript 之外有其他解决方案。尤其是如果已经有像 Scrollama 这样的库可以为你完成这项工作的话。

通常,JS 代码使页面更具动态性。例如:仅使用 CSS + HTML 无法根据 div 的位置更改图像背景。

我会在评论中回复你,提供更多信息。我会尽力帮忙^^