Edit in JSFiddle

const options = {
	root: null,
  rootMargin: '0px 0px 30px 0px',
  threshold: 0
}

// IntersectionObserver 를 등록한다.
const io = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
  	// 관찰 대상이 viewport 안에 들어온 경우 image 로드
    if (entry.isIntersecting) {
    	console.log(entry);
    	entry.target.src = entry.target.dataset.src;
      observer.unobserve(entry.target);
    }
  })
}, options)

// 관찰할 대상을 선언하고, 해당 속성을 관찰시킨다.
const images = document.querySelectorAll('.image');
images.forEach((el) => {
  io.observe(el);
})
<div class="example">
  <h1 class="title">Scroll Down <span class="arrow">👇</span></h1>
  <img src="https://picsum.photos/600/400/?random?0" alt="random image" class="image-default">
  <img data-src="https://picsum.photos/600/400/?random?1" alt="random image" class="image">
  <img data-src="https://picsum.photos/600/400/?random?2" alt="random image" class="image">
  <img data-src="https://picsum.photos/600/400/?random?3" alt="random image" class="image">
  <img data-src="https://picsum.photos/600/400/?random?4" alt="random image" class="image">
  <img data-src="https://picsum.photos/600/400/?random?5" alt="random image" class="image">
  <img data-src="https://picsum.photos/600/400/?random?6" alt="random image" class="image">
  <img data-src="https://picsum.photos/600/400/?random?7" alt="random image" class="image">
</div>
.example {
  padding-top: 400px;
  padding-bottom: 100px;
  width: 100%;
  background: #eee;
  position: relative;
}

.title {
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
}

.arrow {
  display: inline-block;
  animation: bounce 1s infinite ease;
}

.image {
  margin: 20px 0;
  padding: 20px 0;
  display: block;
  height: 100%;
  min-height: 60px;
  width: 100%;
}

.image-default {
  padding: 20px 0;
  display: block;
  width: 100%;
}