Я создал свой собственный маленький слайдер изображений, и чтобы заставить загрузчик работать, мне пришлось создать addEventListener и затем добавить загруженное изображение в DOM.

Однако в этом сценарии есть ошибка: когда для загрузки изображения требуется некоторое время, и пользователь нажимает на него до его загрузки , чтобы увидеть следующее изображение, прослушиватель событий все еще работает в фоновом режиме, и когда изображение полностью загружается, оно перезаписывает то, на что в данный момент смотрит пользователь.

Мой HTML:

<template name="ImageGallery">
    {{#each galleryImages}}
        {{#if viewingImage}}
            {{> Image}}
        {{/if}}
    {{/each}}
</template>

<template name="Image">
    <div class="image-in-gallery">LOADING</div>
</template>

Проверка, является ли «изображение», которое пользователь хочет , увидеть тем, которое мы нажали в итерации each (таким образом, отображая его):

Template.ImageGallery.helpers({
    viewingImage: function() {
        var self = this
        var galleryImages = Template.parentData().galleryImages
        var renderImage = false
        var viewing = Template.instance().viewingImage.get()
        var posOfThisImage = lodash.indexOf(galleryImages, self)
        if (viewing === posOfThisImage) {
            renderImage = true
        }
        return renderImage
    }
})

Предоставление пользователю возможности видеть следующее «изображение» и цикл, если мы достигли конца:

Template.ImageGallery.events({
    'click .click-to-see-next-image': function(event, template) {
        var viewing = template.viewingImage.get()
        var imageCount = this.galleryImages.length
        var nextImage = ++viewing
        if (nextImage < imageCount) {
            template.viewingImage.set(nextImage)
        }
        else {
            template.viewingImage.set(0)
        }
    }
})

Загрузчик:

Template.Image.onRendered({
    var imageUrl = Template.currentData().name + Template.parentData().name + '.jpg'
    var imageObj = new Image()
    imageObj.src = imageUrl

    imageObj.addEventListener('load', function() {
        $('.image-in-gallery').empty()
        $('.image-in-gallery').append($(imageObj))
    }
})

Вы можете увидеть, в чем заключается проблема: empty() и append(), конечно, перезаписывают изображение, на которое смотрит пользователь в данный момент, и устанавливают его в соответствии с тем, что будет загружено в следующий раз.

Я хочу как-то добавить break в функцию addEventListener, чтобы увидеть, является ли загруженное изображение тем, которое хочет видеть пользователь. Но есть две проблемы:

1) Переменная ReactiveVar недоступна в этом шаблоне. Нужно ли мне использовать переменную Session?

2) Понятия не имею, как сломать.

Кто-нибудь может помочь?

5
Yeats 18 Дек 2015 в 13:26

4 ответа

Лучший ответ

Я думаю, что в вашем коде есть два недостатка:

  1. Вы никогда не удаляете событие load, когда ваш шаблон уничтожен. Таким образом, даже если ваш шаблон уничтожен из-за того, что вы нажали, чтобы получить следующее изображение, оно все равно загружается в ваш браузер и запускается событие load. уничтоженные элементы removeEventListener

  2. Вы используете $ в своем onRendered. $ ссылается на глобальный DOM, тогда как это будет только $. ищите локальный DOM вашего шаблона. Если бы вы использовали this.$, это должно было привести к ошибке, потому что ваш локальный DOM был разрушен и неправильное изображение не было бы показано.

Вот как я бы это сделал: https://github.com/darkship/ImageGallery

2
Community 23 Май 2017 в 12:15

Используйте тег img / fake / hidden img для начальной загрузки следующего изображения и прослушивайте событие загрузки этого элемента, после того как изображение было загружено в этот элемент, оно кэшируется, и вы можете поместить его в зритель .

Подсказка: для фальшивого элемента вы должны использовать

.fake-img {visibility: hidden; position: absolute}

Для тега скрытого изображения некоторые браузеры не загружают изображения, если правило display: none

Надеюсь, это поможет..

Но я также согласен, что вы должны подумать о своей архитектуре и постараться не использовать манипуляции с jquery dom в метеоре.

1
webdeb 22 Дек 2015 в 22:40

Прочтение вашего вопроса заставило меня задуматься о том, что, возможно, возможно перестроить вашу шаблонную установку без необходимости использования этого сложного кода в шаблонах и устранить основную причину - неспособность понять, какое изображение в данный момент видно, и легко установить следующее, которое будет видно ,

Я обнаружил, что лучше всего забыть о попытке доступа к родительскому контексту данных из дочерних шаблонов, потому что ReactiveVar в любом случае недоступен через контексты данных (как вы, несомненно, узнали). И иерархии контекста данных зависят от Blaze html, поэтому не рекомендуется использовать их таким образом.

А другой крайностью является наличие переменной Session, которая была бы очень глобальной.

К счастью, есть средний путь.

Определите ReactiveVar вне пространства имен Template, и тогда он будет одинаково доступен как родительским, так и дочерним шаблонам. Это особенно хорошо работает, если вы пытаетесь написать пакет и не хотите загрязнять глобальное пространство имен Session.

Например, поместите это в hello.html:

<head>
  <title>rangetest</title>
</head>

<body>
  <h1>Welcome to RangeTest!</h1>

  {{> hello}}
  {{> rangeList}}
</body>

<template name="hello">

  <p>And another test {{anotherTest}}</p>
</template>

<template name="rangeList">
  {{#each ranges}}
    {{> range}}
  {{/each}}
</template>


<template name="range">
  <p>{{name}}</p>  
  <input type="range" value="{{value}}" min="1" max="10">
</template>

И это в hello.js

if (Meteor.isClient) {
  anotherDict = new ReactiveDict('another');

  anotherDict.set('hey',2);

  Template.hello.helpers({  
    anotherTest: function() {
      return anotherDict.get('hey');
    }
  });

  Template.rangeList.helpers({
    ranges: function() {
      var ranges = [
        {
          'name':'first',
          'value': 5
        },
        {
          'name':'second',
          'value': 6
        },
        {
          'name':'third',
          'value': 7
        },
        {
          'name':'fourth',
          'value': 8
        }
      ];
      return ranges;
    },
  });

  Template.range.events({
   'input input[type="range"]': function(e,t) {  
    var value = parseInt(e.target.value);    
    anotherDict.set('hey',value);    
   }
  });
}    

Вы увидите, что реактивные переменные хорошо распространяются между шаблонами.

Возможно, это не ответит на ваш вопрос о прослушивателях событий, но, надеюсь, вы сможете заменить добавленный вручную прослушиватель событий Template.Image.events({ прослушивателями событий после реализации реактивности, как я предлагал, и они будут тесно связаны с конкретными дочерний шаблон и не будет никакого способа, которым они будут запускаться непредсказуемо.

Постскриптум У меня был пример использования ReactiveDict и входов диапазона, так как это был случай, когда мне нужно было решить аналогичную проблему. Я исследовал все три варианта, и это был тот, который, наконец, сработал и чувствовал себя более или менее метеоритно.

4
Martins Untals 21 Дек 2015 в 21:45

Я вижу, вероятно, проблема в самой архитектуре.

Вы должны использовать две разные структуры данных. Отделите загрузку от рендеринга.

Допустим, galleryImages - это данные изображений, вы должны выполнять их итерацию только для того, чтобы загружать изображения на стороне клиента и помещать их в другой массив загруженных изображений.

Затем шаблон должен повторяться и взаимодействовать с нагруженным изображениями.

Извините, но я не знаком с метеором, я не могу помочь с кодом.

2
Davide Ungari 20 Дек 2015 в 11:01