Предотвращение одновременной прокрутки браузера и flash-контента, «расширенный курс»
Запись от KumoKairo размещена 23.09.2013 в 15:31
Продублирую, пожалуй, сюда эту свою "статейку" с хабра. Может кому-то пригодится. А может найдутся недочеты и исправления.
=====================
Совсем не думал, что решая такой простой (как мне казалось) вопрос, придется здорово потрудиться и пораскинуть мозгами. Но в итоге получилась полноценная библиотека, которая адекватно работает со всеми видами flash - плагинов на странице (включая Pepper flash в Chrome, о котором позже).
Но обо всем по порядку.
Проблема одновременной прокрутки флеш контента и браузера существует давно, поэтому я был уверен, что есть готовое решение "из коробки".
Немного погуглив получаем желаемый результат - MouseWheelTrap. Библиотека отслеживает положение курсора, и если он находится над flash-контентом, отключает прокрутку в браузере, оставляя flash право единолично использованть ролик мыши. Скачиваем, подключаем, все работает.
Просто?
Отнюдь.
Первым неработоспособность блокировки заметил коллега - спросил, почему не работает скролл мышкой. Решил проверить на других машинах - проблема оказалась серьезней чем я думал сначала - на большинстве скролл так же не работал, причем только в Chrome.
Chrome в этом плане здорово переплюнул IE8, поскольку IE с задачей справлялся на ура
Виной всему оказался плагин Pepper flash, который включается в сборку браузера Chrome начиная с версии 11.3. У Pepper flash своя API и "повышенная безопасность". Видимо как раз эта "безопасность" и стала корнем проблемы. Продолжив поиски решения, нашел большое количество упоминаний словосочетаний "Buggy pepper flash" и прочих не очень лестных замечаний в адрес этого флеш плагина. И очень удивило, что на Stackoverflow не было ни одного полноценного ответа по этой теме:
вопрос 1
вопрос 2
...
Это меня здорово вдохновило на поиск и написание собственного решения.
Первым что пришло в голову было следующее: не блокировать стандартную прокрутку браузера с помощью стандартной
Код:
event.preventDefault(); //Для всех браузеров, кроме IE event.returnValue = false;//Для IE
Идея, конечно, костыль с гвоздями, но как то начинать решение проблемы надо.
Оказалось что вызывать функции прокрутки страницы ни из обработчика события перемещения ролика мыши, ни из flash с помощью ExternalInterface нельзя.
Оно и к лучшему, решение в любом случае было далеко от изящного и оригинального.
Но немного разобрав ту самую библиотеку, удалось ее здорово улучшить. Основная её проблема была в том, что после каждого движения мыши она отправляла на выполнение довольно здоровый кусок кода на javascript, что в данном случае не имело совершенно никакого смысла:
JAVASCRIPT - это "строка" кода, которая в развернутом состоянии занимает 41 строчку. Немного расточительно при каждом движении курсора мыши отправлять код с определением функций на страницу.
Пришлось перенести его в инициализацию библиотеки и в последствии там же и оставить.
После этого я решил проверить, каким именно образом отключается стандартная прокрутка в Chrome и чем это действие отличается от аналогичного в других браузерах.
Оказалось что Pepper flash (читай flash в Chrome) просто не получает событий передвижения колесика, если мы отключаем стандартную прокрутку с помощью
Код:
event.preventDefault();
Логика решения:
- Ловим событие прокрутки колесика мыши
- Отправляем детали события (дельта прокрутки) во Flash
- Блокируем прокрутку браузера
- Собираем событие прокрутки в flash с нуля
- Диспетчиризуем событие на сцену
Для отладки я использовал внешний .js скрипт, в конечной версии все функции js регистрируются на странице из Flash.
Отлично, приступим.
Основная часть работы возлагается на "внешний" код на странице:
Код:
var browserScrollAllow = true; //По умолчанию разрешаем прокручивать страницу /** * Функция вызывается один раз, регистрируем слушателей всевозможных событий прокрутки колесика */ function registerEventListeners() { if (window.addEventListener) { window.addEventListener('mousewheel', wheelHandler, true); window.addEventListener('DOMMouseScroll', wheelHandler, true); window.addEventListener('scroll', wheelHandler, true); } window.onmousewheel = wheelHandler; document.onmousewheel = wheelHandler; } /** * Наша основная функция, в которой ловим прокрутку и опционально заставляем flash * сгенерировать свое событие прокрутки */ function wheelHandler(event) { var delta = event.wheelDeltaY; if (!event) { event = window.event } if (!browserScrollAllow) { if (window.chrome) { document.getElementById('flashObject').scrollHappened(delta); } if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } } } /** * Эту функцию вызываем из flash каждый раз, когда курсор мыши заходит на область flash или покидает ее */ function allowBrowserScroll(allow) { browserScrollAllow = allow; }
Код:
if (window.chrome) { document.getElementById('flashObject').scrollHappened(delta); }
Теперь то, что находится внутри flash (код обработчиков перемещения мыши и ухода курсора со сцены в данном случае особой значимости не несет и может быть опущен):
/** *Функция инициализации "библиотеки" */ function initialize(stage: Stage): void { if (ExternalInterface.available) { nativeStage = stage; stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseOverStage); //Курсор мыши над сценой stage.addEventListener(Event.MOUSE_LEAVE, mouseLeavesStage); //Курсор мыши за пределами сцены stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel); //Проверочная функция прокрутки колесиком ExternalInterface.addCallback("scrollHappened", scrollHappened); } else { throw new UninitializedError(NO_EXTERNAL_INTERFACE_ERROR); } } /** * Функция диспетчиризации нового события прокрутки */ function scrollHappened(wheelDelta: Number): void { nativeStage.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_WHEEL, true, false, nativeStage.mouseX, nativeStage.mouseY, null, false, false, false, false, wheelDelta / 40)); }
Таким образом мы получаем и обрабатываем событие прокрутки до того, как вызываем
Код:
event.preventDefault();
Все?
Не совсем.
Чуть позже оказалось, что на Mac OS-x присутствует та же проблема. При этом уже не в Chrome а во всех браузерах по умолчанию. Тут уже не получилось бы отделаться простой проверкой на тип браузера.
На помощь пришла стандартная функция .os из библиотеки
flash.system.Capabilities
Определяем, сидит ли пользователь с OS-X (AS3):
Немного поправляем функцию регистрации слушателей, заставляем принимать булеву переменную isMac в качестве параметра (JS):
И добавляем дополнительное условие для диспетчиризации собственного события:
if (window.chrome || isMac) { document.getElementById('flashObject').scrollHappened(delta); }
В итоге, если немного отполировать код(добавить весь JS код в AS3 в виде строковой величины и вызывать с помощью , и добавить входной параметр - flashID для большей гибкости) , получим работоспособную библиотеку, полноценного аналога которой я почему-то не смог найти на просторах интернета
Ссылка на репозиторий GitHub
Всего комментариев 11
Комментарии
23.09.2013 22:40 | |
24.09.2013 21:18 | |
У меня все три работают в хроме. Первая не работает в IE 9, не отпускает фокус с флешки. Не могу проверить какие не работают в MacOS. Можете ли вы сказать какие?
|
25.09.2013 00:21 | |
В хроме я тестировал на указанных вами основаниях.
Что помешало проверить на MacOS два других решения, приведенных мною в первом посте? |
25.09.2013 07:30 | |
Я ж написал что только с пеппер флеш такой косяк. Поэтому и не было возможности отследить проблему изначально, у меня то тоже дебаг версия стояла.
Проверить помешало отсутствие Мак машины дома) Проверил на маке - по двум последним ссылкам вроде работает (проверил в сафари и опере) |
|
Обновил(-а) KumoKairo 25.09.2013 в 10:13
|
26.09.2013 00:45 | |
Скорее всего потому что не встретил эти два решения в своих долгих поисках по просторам интернета)
Проблема боком стояла, попробовал 4-5 библиотек и ни одна до конца не работала. Кстати, по сопутствующим статьям можно сделать вывод, что с wmode=direct они не тестились (знаю что про wmode в моей "статье" ни слова, но в нашем приложении из-за использования старлинга приходится использовать именно direct) и может быть в этом и есть корень проблемы. Надо будет попробовать прикрутить к тому проекту ради интереса. Вопрос изначально не стоял - "сделать свою либу", я честно и долго искал готовое решение, но сроки поджимали а решения так и не было. Очень странно, что не наткнулся на два предложенных вами источника, искал довольно долго и всевозможные вариации В общем я не был бы так категоричен. Я делал это не из-за жажды наживы или легких денег) |
|
Обновил(-а) KumoKairo 26.09.2013 в 01:15
|
26.09.2013 01:09 | |
Тем не менее, я рад, что вы двинулись в этом направлении.
|
26.09.2013 11:31 | |
Встроил в обычный демо проект - либа по второй ссылке требует настройки политики безопасности для локального тестирования, выдает ошибку нарушения изолированной среды. При работе с удаленного сервера на MacOS есть очень неприятная особенность создавать "инерцию" прокрутки (в стандартном Feathers листе). То есть делаем прокрутку, перестаем прокручивать, выводим курсор за рамки флешки - страница прокручивается по "инерции" - видимо в этой либе идут от противного - передают в браузер информацию о прокрутке из флеш, а не наоборот. Плюс не всегда схватывает саму прокрутку (у меня в обычном дебаг плеере в фаерфоксе на винде бывает проскакивает так, что не схватывается) (ссылка)
Либа по третьей ссылке вообще работает как автономная библиотека для ембединга флеш объекта на страницу, альтернатива SWFObject. Я, пожалуй, не буду отказыватся от более проверенного способа ембединга, я думаю тут вы тоже поймете. В общем все интересней чем казалось сначала |
|
Обновил(-а) KumoKairo 27.09.2013 в 22:31
|
25.05.2017 03:40 | |
Столкнулся с тем, что MouseWheelTrap не работает в Chrome. Но теперь репозиторий из поста недоступен. Автор, можешь перезалить?
|
Последние записи от KumoKairo