UiImg.vue 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. <template>
  2. <div ref="imageContainer" class="ui-image relative">
  3. <div class="flex justify-center items-center">
  4. <slot v-if="state.loading" name="loading">
  5. <div
  6. class="absolute h-full rounded-lg bg-input-dark w-full animate-pulse"
  7. ></div>
  8. </slot>
  9. <slot v-else-if="state.error" name="error">
  10. <p class="text-lighter text-center">Failed to load image</p>
  11. </slot>
  12. <div
  13. v-else
  14. :style="{
  15. backgroundImage: `url(${src})`,
  16. backgroundSize: contain ? 'contain' : 'cover',
  17. }"
  18. v-bind="{ role: alt ? 'img' : null, 'aria-label': alt }"
  19. class="h-full absolute top-0 left-0 w-full bg-no-repeat bg-center"
  20. >
  21. <slot></slot>
  22. </div>
  23. </div>
  24. </div>
  25. </template>
  26. <script>
  27. import { ref, shallowReactive, onMounted } from 'vue';
  28. export default {
  29. props: {
  30. src: {
  31. type: String,
  32. default: '',
  33. },
  34. alt: {
  35. type: String,
  36. default: '',
  37. },
  38. lazy: Boolean,
  39. contain: Boolean,
  40. },
  41. emits: ['error', 'load'],
  42. setup(props, { emit }) {
  43. const imageContainer = ref(null);
  44. const state = shallowReactive({
  45. loading: true,
  46. error: false,
  47. });
  48. function handleImageLoad() {
  49. state.loading = false;
  50. state.error = false;
  51. emit('load', true);
  52. }
  53. function handleImageError() {
  54. state.loading = false;
  55. state.error = true;
  56. emit('error', true);
  57. }
  58. function loadImage() {
  59. const image = new Image();
  60. image.onload = () => handleImageLoad(image);
  61. image.onerror = handleImageError;
  62. image.src = props.src;
  63. }
  64. const observer = new IntersectionObserver((entries) => {
  65. entries.forEach((entry) => {
  66. if (entry.isIntersecting) {
  67. const { target } = entry;
  68. loadImage();
  69. observer.unobserve(target);
  70. }
  71. });
  72. });
  73. onMounted(() => {
  74. if (props.lazy) observer.observe(imageContainer.value);
  75. else loadImage();
  76. });
  77. return {
  78. state,
  79. imageContainer,
  80. };
  81. },
  82. };
  83. </script>