まずはコードから

<span id="loading" class="icon loading" />
<span id="icon" class="icon hidden" style="background-image: url('https://example.com/iconurl')" />

<style>
.hidden {
  display: none;
}
.icon {
  background-size: 100% 100%;
}
/* この辺から拝借 https://ant.design/components/skeleton/ */
.loading {
  background: -webkit-gradient(linear, left top, right top, color-stop(25%, #f2f2f2), color-stop(37%, #e6e6e6), color-stop(63%, #f2f2f2));
  background: linear-gradient(90deg, #f2f2f2 25%, #e6e6e6 37%, #f2f2f2 63%);
  background-size: 400% 100%;
  -webkit-animation: loading 1.4s ease infinite;
  animation: loading 1.4s ease infinite;
}
@-webkit-keyframes loading {
  0% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0 50%;
  }
}
@keyframes loading {
  0% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0 50%;
  }
}
</style>
// yarn add imagesloaded https://imagesloaded.desandro.com/
import imagesLoaded from 'imagesloaded'

const icon = document.getElementById('icon')
const loading = document.getElementById('loading')
imagesLoaded(icon, { background: true }, () => {
  loading.classList.add('hidden')
  icon.classList.remove('hidden')
})

解説

まずはパッケージの解説

imagesloadedというパッケージがあり、これは画像読み込みを検知してくれるやつ
background-imageであればoptionsに{ background: true }としてあげると背景画像の読み込みも待ってからコールバック関数が呼ばれる

動作の解説

画像読み込みをしている間はなんかそれっぽいロードしてますよ感のあるやつを表示しておいて、画像を表示するやつは非表示にしておく(cssアニメーション便利)
読み込みが終わったらクラスを足したり消したりして画像を表示する

注意点?

画像を表示するタグで読み込みのスタイルを適用しないのは読み込みが完了した直後にCSSアニメーションが少し残ることがあり、その際に画像が横に引っ張られたりして見栄えが少し悪いから
よくあるゲームとかのロードとかと発想は同じ

最後に

imagesloadedは以前はまだバックグラウンドの読み込み待ちに対応してなかったが数年前に対応したらしい
時代が進むにつれていろんなものが便利になっていっていてとても良き
こうしたらもっと良いと思う!というのあったりしたらコメント残してって下しあ