Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > KumoKairo

Рейтинг: 5.00. Голосов: 3.

Предотвращение одновременной прокрутки браузера и 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, что в данном случае не имело совершенно никакого смысла:

Код AS3:
if (ExternalInterface.available) {
    ExternalInterface.call("eval", JAVASCRIPT);
    return;
}
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);
        }
Если мы имеем дело с Chrome, то уведомляем флешку о том, что произошла прокрутка и передаем значение перемещения.

Теперь то, что находится внутри flash (код обработчиков перемещения мыши и ухода курсора со сцены в данном случае особой значимости не несет и может быть опущен):

Код AS3:
/**
*Функция инициализации "библиотеки"
*/
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):

Код AS3:
isMac = Capabilities.os.toLowerCase().indexOf("mac") != -1;
Немного поправляем функцию регистрации слушателей, заставляем принимать булеву переменную isMac в качестве параметра (JS):

Код AS3:
function registerEventListeners(inputIsMac) {
    isMac = inputIsMac;
}
И добавляем дополнительное условие для диспетчиризации собственного события:

Код AS3:
if (window.chrome || isMac) {
     document.getElementById('flashObject').scrollHappened(delta);
}

В итоге, если немного отполировать код(добавить весь JS код в AS3 в виде строковой величины и вызывать с помощью
Код AS3:
ExternalInterface.call("eval",...)
, и добавить входной параметр - flashID для большей гибкости) , получим работоспособную библиотеку, полноценного аналога которой я почему-то не смог найти на просторах интернета

Ссылка на репозиторий GitHub
Всего комментариев 11

Комментарии

Старый 23.09.2013 22:40 dimarik вне форума
dimarik
 
Аватар для dimarik
А это не подходит?
Вот тут еще есть.
И здесь что-то.
Старый 23.09.2013 23:10 KumoKairo вне форума
KumoKairo
 
Аватар для KumoKairo
Нет, во время разработки все что было перепробовал (эти примеры в частности) - часть не работают в Chrome, часть в MacOS, какие то в IE

Там еще был вариант, китайцы сделали как-то через LocalConnection и передачу каких-то байтовых массивов, но работало только на MacOS. Пришлось даже качать с какого то китайского репозитория, где ни слова на английском не было (наугад нажимал все подряд, но библиотека в итоге скачалась). Но даже эта библиотека не давала 100% работоспособности
Старый 24.09.2013 21:18 dimarik вне форума
dimarik
 
Аватар для dimarik
У меня все три работают в хроме. Первая не работает в IE 9, не отпускает фокус с флешки. Не могу проверить какие не работают в MacOS. Можете ли вы сказать какие?
Старый 24.09.2013 22:58 KumoKairo вне форума
KumoKairo
 
Аватар для KumoKairo
Скорее всего вы проверяете работоспособность с дебаг плагином - в нем все работает, да. По этой причине я и не предполагал что на самом деле, с обычным пеппер флеш плагином, такие косяки.

Первая точно не работает в MacOS, так же точно не работает та, которую я привел в статье, насчет остальных не скажу с точностью
Старый 25.09.2013 00:21 dimarik вне форума
dimarik
 
Аватар для dimarik
В хроме я тестировал на указанных вами основаниях.


Что помешало проверить на MacOS два других решения, приведенных мною в первом посте?
Старый 25.09.2013 07:30 KumoKairo вне форума
KumoKairo
 
Аватар для KumoKairo
Я ж написал что только с пеппер флеш такой косяк. Поэтому и не было возможности отследить проблему изначально, у меня то тоже дебаг версия стояла.

Проверить помешало отсутствие Мак машины дома) На рабочем месте проверю)
Проверил на маке - по двум последним ссылкам вроде работает (проверил в сафари и опере)
Обновил(-а) KumoKairo 25.09.2013 в 10:13
Старый 25.09.2013 23:16 dimarik вне форума
dimarik
 
Аватар для dimarik
Прошу прощения, я немного запутался. Скриншот говорит о том, что я использую papper. При нем отлично работают два последних примера. Какие проблемы с papper вы имеете ввиду?

Вы утверждаете, что проверили в том числе решения, ссылки на которые перечислены мною в первом комментарии на адекватность некоторому ожидаемому вами поведению. Проверили и поняли, что ожидание не соответствует действительности и эти решения не подходят. Вы начинаете писать свой велосипед. Теперь я проверяю эти решения на такую же адекватность, я соблюдаю все условия в которых проверяли вы. Я нахожу по крайней мере два решения, соответствующих условиям задачи. В чем я виноват, что заставляю вас оправдываться?
Старый 26.09.2013 00:45 KumoKairo вне форума
KumoKairo
 
Аватар для KumoKairo
Скорее всего потому что не встретил эти два решения в своих долгих поисках по просторам интернета)

Проблема боком стояла, попробовал 4-5 библиотек и ни одна до конца не работала. Кстати, по сопутствующим статьям можно сделать вывод, что с wmode=direct они не тестились (знаю что про wmode в моей "статье" ни слова, но в нашем приложении из-за использования старлинга приходится использовать именно direct) и может быть в этом и есть корень проблемы.

Надо будет попробовать прикрутить к тому проекту ради интереса.

Вопрос изначально не стоял - "сделать свою либу", я честно и долго искал готовое решение, но сроки поджимали а решения так и не было. Очень странно, что не наткнулся на два предложенных вами источника, искал довольно долго и всевозможные вариации

В общем я не был бы так категоричен. Я делал это не из-за жажды наживы или легких денег)
Обновил(-а) KumoKairo 26.09.2013 в 01:15
Старый 26.09.2013 01:09 dimarik вне форума
dimarik
 
Аватар для dimarik
Тем не менее, я рад, что вы двинулись в этом направлении.
Старый 26.09.2013 11:31 KumoKairo вне форума
KumoKairo
 
Аватар для KumoKairo
Встроил в обычный демо проект - либа по второй ссылке требует настройки политики безопасности для локального тестирования, выдает ошибку нарушения изолированной среды. При работе с удаленного сервера на MacOS есть очень неприятная особенность создавать "инерцию" прокрутки (в стандартном Feathers листе). То есть делаем прокрутку, перестаем прокручивать, выводим курсор за рамки флешки - страница прокручивается по "инерции" - видимо в этой либе идут от противного - передают в браузер информацию о прокрутке из флеш, а не наоборот. Плюс не всегда схватывает саму прокрутку (у меня в обычном дебаг плеере в фаерфоксе на винде бывает проскакивает так, что не схватывается) (ссылка)

Либа по третьей ссылке вообще работает как автономная библиотека для ембединга флеш объекта на страницу, альтернатива SWFObject. Я, пожалуй, не буду отказыватся от более проверенного способа ембединга, я думаю тут вы тоже поймете.

В общем все интересней чем казалось сначала
Обновил(-а) KumoKairo 27.09.2013 в 22:31
Старый 25.05.2017 03:40 Alex626 вне форума
Alex626
Столкнулся с тем, что MouseWheelTrap не работает в Chrome. Но теперь репозиторий из поста недоступен. Автор, можешь перезалить?
 

 


Часовой пояс GMT +4, время: 17:35.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.