Универсальный GrayScale с плавной работой (затухание и проявление), принцип работы?
Приветствую.
Появилась задача сделать скрипт. который бы делал картинки на сайте серыми по-умолчанию (т.е. после загрузи страницы) и цветными при наведении мышкой. Универсального рецепта, который одинаково бы работал во всех браузерах (читай в IE тоже) - не нашел. Если кто-то знает такой скриптег - прошу подсказать. Но это лишь быстрый способ решения проблемы... Отрыв один такой скрипт, который работает не особо универсально, я заглянул внутрь и понял, что большой проблемы его поправить - нет. Но понимания джаваскрипта у меня полного нет. И проблема в том, что не понимаю какого рода информацию мне нужно искать... а вопрос вот в чем. Я могу сделать черно-белую картинку по наведению/загрузке страницы - не проблема. Но не понимаю как сделать плавный переход от черно-белого состояния к цветному. Каким образом осуществляются подобные плавные переходы? Наложить невидимую цветную картинку поверх серой, и просто плавно проявлять? Т.е. через opacity? Или есть более адекватные и красивые методы? Если предложенный мною вариант адекватен, то подскажите, пожалуйста, как в Jquery работать с таймерами? Ибо эту часть понимаю плохо :( Спасибо. |
Цитата:
Цитата:
http://jquery-docs.ru/Effects/animat...easingcallback |
Почти написал, судя по всему... но прошу помощи. Не получается собрать всё воедино. здесь выложил результат работы, вопросы следующие:
|
Цитата:
|
Как раз проблема в том, что по-отдельности у меня все работало :(
<script type="text/javascript"> $(document).ready(function() { $('.gray').hover(hoverIn(this), hoverOut());}); }); </script> Не могу понять как правильно передать указатель на картинку для функции hover, если я передаю this - то передается ссылка на файл (на локальном компе ссылка передается именно на файл html. Вида file:///d:/garbage/.../file.html Если не передаю ничего, а просто в функции вызываемой hover-ом используют $(this).animate() - то скрипт падает с ошибками. Вывод alert(this) показывает object window. Как передать внутрь функции вызывающей анимацию ссылку на конкретную картинку? Ибо селектором получить нужный элемент - нереально. На сайте картинок для которых пишется скрипт будет много, и взять конкретную по селектору - невозможно. |
Вопрос снят - нашел косяк. И в целом сделал иначе - перевесил привязку hoverа на момент создания элемента. Плюс, как оказалось, нужно делать не
$('.gray').hover(hoverIn(), hoverOut());а $('.gray').hover(hoverIn, hoverOut); Теперь разбираюсь с вызовом функции в правильный момент... |
Цитата:
$('.gray').hover(function (){ hoverIn(); }, function (){ hoverOut(); }); |
Цитата:
$('.gray').hover(function (){ (function(){ hoverIn() })(); }, function (){ (function(){ hoverOut() })(); }); Но.. только зачем? |
Цитата:
|
Цитата:
$('.gray').hover( $.proxy(hoverIn, this, arg1, arg2)), $.proxy(hoverOut, this, arg1, arg2)) ); ES5: $('.gray').hover( hoverIn.bind(this, arg1, arg2), hoverOut.bind(this, arg1, arg2) ); Но да, анонимные функции тож довольно удобно. |
Учебный вариант заработал во всех нужных браузерах IE 7+ (6го нет что бы проверить, но и там должно работать), FF, Opera, Chrome, Safari. Посмотреть можно здесь, а скачать здесь (не нашел как файл к сообщению прикрепить).
Господа спецы, прошу, по-возможности, глянуть как оно работает. Если есть какие-то кривости в коде, опасные места - подскажите, пожалуйста. Если можно - с общими советами как это исправить (работать вместо меня не прошу, но полезные советы - это хорошо). Я бы хотел выложить данный плагин для свободного использования, но для этого его нужно довести до совершенства. А в JS я весьма слаб... потому рассчитываю на вашу помощь :) |
Давай сюды код тогда.
|
Вызов плагина, параметром передается селектор, для которого нужно применить grayScale.
$(document).ready(function() { makeGray('body div img'); }); Подключаемый файл, где собственно описана вся логика работы. var grayTryCount = 0; var timerID = false; function makeGray(selector) { if (grayTryCount <= 3) { grayTryCount +=1; $(selector).each(function(){ if ((this.complete) && (!$(this).hasClass('makeGrayDone')) && (!$(this).hasClass('gray'))){ makeCopy(this); } else { if (!timerID) { timerID = setTimeout(function() {makeGray('body div img')}, 1000); } } }); } } function makeCopy(img) { var canvas2DSupported = !!window.CanvasRenderingContext2D; if (canvas2DSupported) { var newImg = makeCanvasCopy(img); } else { var newImg = makeIMGCopy(img); } var offset = $(img).offset(); $(img).parent().append(newImg); $(img).addClass('makeGrayDone'); $(newImg).offset(offset); $(newImg).hover(hoverIn, hoverOut); } function makeIMGCopy(imgObj) { var newImg = document.createElement('img'); newImg.src = $(imgObj).attr('src'); $(newImg).attr('class', 'gray'); newImg.style.filter ='progid:DXImageTransform.Microsoft.BasicImage(grayScale=1)'; return newImg; } function makeCanvasCopy(imgObj) { var newImg = document.createElement('img'); var canvas = document.createElement('canvas'); var canvasContext = canvas.getContext('2d'); var imgW = imgObj.width; var imgH = imgObj.height; canvas.width = imgW; canvas.height = imgH; canvasContext.drawImage(imgObj, 0, 0); var imgPixels = canvasContext.getImageData(0, 0, imgW, imgH); for(var y = 0; y < imgPixels.height; y++){ for(var x = 0; x < imgPixels.width; x++){ var i = (y * 4) * imgPixels.width + x * 4; var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3; imgPixels.data[i] = avg; imgPixels.data[i + 1] = avg; imgPixels.data[i + 2] = avg; } } canvasContext.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height); newImg.src = canvas.toDataURL(); $(newImg).attr('class', 'gray'); return newImg; } function createCanvas(imgObj) { var canvas = document.createElement('canvas'); var canvasContext = canvas.getContext('2d'); var imgW = imgObj.width; var imgH = imgObj.height; canvas.width = imgW; canvas.height = imgH; canvasContext.drawImage(imgObj, 0, 0); var imgPixels = canvasContext.getImageData(0, 0, imgW, imgH); for(var y = 0; y < imgPixels.height; y++){ for(var x = 0; x < imgPixels.width; x++){ var i = (y * 4) * imgPixels.width + x * 4; var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3; imgPixels.data[i] = avg; imgPixels.data[i + 1] = avg; imgPixels.data[i + 2] = avg; } } canvasContext.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height); imgObj.src = canvas.toDataURL(); //return canvas.toDataURL(); } function hoverIn(obj) { $(this).animate({ opacity: 0 }, {duration: 500, step: function(now, fx) { // Every step of the opacity animation we'll get the current // opacity as the 'now' argument. var opacity = Math.round(now * 100); $(fx.elem).css('-ms-filter', 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + opacity + ')'); } }); } function hoverOut() { $(this).animate({ opacity: 1 }, {duration: 500, step: function(now, fx) { var opacity = Math.round(now * 100); $(fx.elem).css('-ms-filter', 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + opacity + ')'); } }); } |
master_alf,
А картинка пока только с того же домена ? |
Да, только с этого же домена.
Я, если честно, слабо представляю зачем нужно грузить картинки с других доменов и обрабатывать их подобным образом... но может это дело в слабой фантазии? :D В принципе, если в готовом плагине лучше, что бы оно было - погуглю как делается. Встречал рецепты. |
master_alf,
На большинстве халявных сайтах и форумов, нет загрузки изо на текущий домен Пример Укоз, mybb.ru |
Цитата:
На хабре видал статейку, как реализуемо для свежих версий браузеров => http://habrahabr.ru/post/120917/ |
Цитата:
2) Раз jQuery, то и оформи как плагин к jQuery. Чтобы вызов был через $('div img').makeGray() Цитата:
Цитата:
Передать в анонимную функцию - обертку ссылку на jQuery, а внутри уже использовать знак доллара. Смотри как это сделано в других плагинах. Вообще, раз ты делаешь плагин, хоть бы не поленился и изучил как их делают другие. Цитата:
Цитата:
Цитата:
Цитата:
Где используется функция createCanvas ? Цитата:
Перед .animate() нужно делать .stop() чтоб не образовывалось очереди. |
В WebKit и вроде 10м ишаке (не уверен) есть поддержка css mask, нужно проверять поддержку и по возможности использовать. Это в тыщу раз быстрее чем работа с пикселями.
$(newImg).attr('class', 'gray') Слишком общий css-класс. Он может быть определен у юзера. Вообще, зачем тут css-классы назначать? |
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
|
Цитата:
|
Цитата:
<script type="text/javascript" src="http://yandex.st/jquery/1.8.1/jquery.min.js"></script> <script type="text/javascript"> //Плагин выдачи списка img, отмеченных неким классом ( в данном случае передаём в вызове класс .imgGray $.fn.makeGray = function() { var imgArray = this; //this - переданый объект из jQuery ( При ниже вызове плагина это $(".imgGray") var arrOut = []; imgArray.each(function() { arrOut.push(this.src) }); alert(arrOut.join(",\n")) } $(document).ready(function(){ $(".imgGray").makeGray (); //Вызов и запуск плагина }); </script> <img class="imgGray" src="s2.png" /> <img class="imgGray" src="s1.png" /> |
Deff,
Спасибо, но уже бессмысленно :) нашел объяснение на сайте jQuery :) переделал. Пока без кроссдоменных картинок. Просто навел порядок во всем, на что указали. Вот код. (function( $ ){ $.fn.grayScale = function() { $(this).each(function() { this.onload = function() makeCopy(this); }); function makeCopy(img) { var canvas2DSupported = !!window.CanvasRenderingContext2D; if (canvas2DSupported) { var newImg = makeCanvasCopy(img); } else { var newImg = makeIMGCopy(img); } var offset = $(img).offset(); $(img).parent().append(newImg); $(newImg).offset(offset); $(newImg).hover(hoverIn, hoverOut); } function makeIMGCopy(imgObj) { var newImg = document.createElement('img'); newImg.src = imgObj.src $(newImg).addClass('grayScaleGray'); newImg.style.filter ='progid:DXImageTransform.Microsoft.BasicImage(grayScale=1)'; return newImg; } function makeCanvasCopy(imgObj) { var newImg = document.createElement('img'); var canvas = document.createElement('canvas'); var canvasContext = canvas.getContext('2d'); var imgW = imgObj.width; var imgH = imgObj.height; canvas.width = imgW; canvas.height = imgH; canvasContext.drawImage(imgObj, 0, 0); var imgPixels = canvasContext.getImageData(0, 0, imgW, imgH); for(var y = 0; y < imgPixels.height; y++){ for(var x = 0; x < imgPixels.width; x++){ var i = (y * 4) * imgPixels.width + x * 4; var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3; imgPixels.data[i] = avg; imgPixels.data[i + 1] = avg; imgPixels.data[i + 2] = avg; } } canvasContext.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height); newImg.src = canvas.toDataURL(); $(newImg).addClass('grayScaleGray'); return newImg; } function hoverIn(obj) { $(this).stop().animate({ opacity: 0 }, {duration: 500, step: function(now, fx) { // Every step of the opacity animation we'll get the current // opacity as the 'now' argument. var opacity = Math.round(now * 100); $(fx.elem).css('-ms-filter', 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + opacity + ')'); $(fx.elem).css('ms-filter', 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + opacity + ')'); } }); } function hoverOut() { $(this).stop().animate({ opacity: 1 }, {duration: 500, step: function(now, fx) { var opacity = Math.round(now * 100); $(fx.elem).css('-ms-filter', 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + opacity + ')'); $(fx.elem).css('ms-filter', 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + opacity + ')'); } }); } }; })( jQuery ); Вот вызов. <script type="text/javascript"> $(document).ready(function() { $('div > img').grayScale(); }); </script> Теперь работает как плагин 8) Но снова появился баг в хроме. Он ругается, что не может определить высоту для создаваемого canvasa, т.к. функция выполняется ДО полной загрузки картинки. Как я понимаю, я где-то неправильно вызываю функцию. Как раз эти самые анонимные функции где-то и должны использоваться? Но смаху решения найти не смог. Объясните, пожалуйста, как правильно вызывать функции из событий (в моем случае image.onload), или же функцию из функции таким образом, что бы они исполнялись только в нужный момент, а не в момент загрузки? |
$(this).each(function() { this.onload = function() makeCopy(this); }); Мне кажется, косяк где-то вот тут... но как правильно сделать - не пойму. |
Цитата:
1. Картинка после вызова $(document).ready(function() { $('div > img').grayScale(); может уже быть загружена(и onload не будет вызываться, поэтому скорее всего вызов нужно делать без обертки в $(document).ready(function() { а обернуть вызов и движение по функции до тега <body> $(this).each(function() { this.onload = function() makeCopy(this); //А тут уже можно парсить и не ждать DOM загрузки }); |
Прежде чем переносить реализацию на img.onload = func() я потестировал работу данного метода.
Просто цепляя на кнопку регистрацию обработчика события. Т.е. $('a').click(function() { var img = document.getElementById('myImage'); img.onload = alert('work!'); }); И такое работало... Судя по тому, что я вижу в хроме, там проблема именно в том, что функция пытается выполниться не после загрузки картинки, а перед этим. Я где-то натыкался на пояснение, что по-разному оформленный вызов функций по-разному отрабатывается. Т.е. что-то пытается отработать сразу, в момент инициализации, а что-то просто загружается, и не покажет багу до момента вызова... Мне кажется, что у меня косяк именно тут. Или я не прав, и говорю не том вообще? |
Нун гуглить , тут пару дней назад на этом форуме разбирали загрузку-подгрузку ожидание картинок
|
Deff,
Я понял о чем ты говоришь. Но это не работает. Проверил все варианты - убрал документ.реди(), переместил вызов в head. Все эти варианты не помогают. Да и не могут. Ведь проблема не в том, что onload Не срабатывает - он срабатывает даже когда всё давно загрузилось, о чем я и говорил своим примером с кликом по ссылке. Дело как раз в том, что скрипт загружаясь До картинки, почему-то инициализируется на выполнение, и пытается получить данные о размерах еще не загруженного изображения. И только в Хроме и Сафари получается баг - т.к. скрипт срабатывает ДО загрузки изображения. Ишак, опера и ФФ - без проблем ожидают загрузки изображений. |
Блин! Разобрался...
Хром, зараза... в общем, дело вот в чем. Если вызов делать вида this.onload = makeCopy(this); Будет ошибка. А если вида this.onload = function() {makeCopy(this)}; Всё нормально! НО! Хром, останавливаясь на ошибке, игнорирует изменения кода если страницу тупо перезапускать, не нажимая "продолжить отладку". Уж не знаю почему так... может это какой-то глюк конкретно моего хрома... в общем - я нашел способ правильной записи. Завтра добавлю поддержку картинок со сторонних сайтов, и, если у спецов замечаний по коду больше не будет - то плагин готов :) Спасибо всем за помощь. |
master_alf,
Может делать иной алгоритм 1. Дожидаемся загрузки страницы, 2. далее создаём - перебираем массив всех картинок 3. Смотрим подгружена картинка или нет Если нет, -вынимаем её из очереди обработки и вешаем onload |
Потестирую с разными картинками текущее решение, если даст где-то сбой, перепишу как ты сказал. Пока же - наконец-то работает! %)
|
Уже конечно намного лучше )
Цитата:
$('img').makeGray().css('border': '1px solid'); Цитата:
Можно просто this.onload = makeCopy; При событии load будет вызывана makeCopy, в которой this укажет на картинку. Однако что будет если картинка уже загрузилась? Я не уверен что обработчик сработает. Поэтому нужно проверять - загрузилась ли картинка (через img.complete кажется, может для ишака иначе). Цитата:
И еще раз спрашиваю - разве без этого всего в ишаке не работает? Ведь jQuery внутри уже сама использует нужные для ишака свойства при анимировании opacity... |
Цитата:
Вернуть целую функцию?.. что за безумный язык! Нет, я понимаю, что ваши советы по существу... но, блин... мои олдскульные устои человека, учившегося кодить на Паскале - ломаются окончательно :blink: Цитата:
Цитата:
Цитата:
Цитата:
Ну либо я находил кривые решения, и сам не смог добиться иным путём. Тут я не могу ничего сказать - совершенно нуб :) P.S. Извиняюсь за, по сути, оффтоп, но, ребят, можете подсказать какую конкретно часть документации/учебника/справочника/самоучителя/пофиг_чего прочитать, что бы врубиться таки во все эти хитросплетения вызовов, колбэков и прочих тонкостей джаваскрипта? Ибо что и как в каких случаях следует вызывать - для меня просто... неочивидно. Например function some() {some code here} где-то вызывать как some, без () - для меня... непонятно - как так-то, вообще?!.. а хотелось бы ориентироваться, что я делаю... |
master_alf,
Думаю нун не спеша для начала выспаться, Утро -вечера мудреннее! |
Цитата:
Цитата:
Цитата:
Какие еще рецепты. Просто убери этот кусок кода, все должно работать без него. В крайнем случае запили тестовый пример прям тут - посмотрим. По части документации - для начала можешь покурить Учебник |
Цитата:
Цитата:
Цитата:
Цитата:
newImg.style.filter ='progid:DXImageTransform.Microsoft.BasicImage(grayScale=1)'; Т.е. просто плавный переход в серый цвет. И не работало именно такое решение, а не с применением прозрачности. Цитата:
P.S. на сайте/форуме нет раздела в духе "вот мой плагин, пользуйтесь на здоровье"? Вижу на форуме место для обсуждения плагинов, но это немного не то. И так оффтопиков развёл. :( P.P.S. прикручу реализацию кросдоменных картинок, и выложу окончательный вариант. Ну или раньше приду с вопросами :D |
Цитата:
|
Цитата:
Код:
Header set Access-Control-Allow-Origin "*" Вопрос - я прав, и это действительно тот способ который ты посоветовал? Или я проглядел что-то более простое... Ибо если нужно перенастроить заголовки ответа сервера, с которого планируется получать свои картинки... То во многих случаях плагин работать не будет. Т.к. доступа к серверу нет, а вряд ли большинство хостеров/админов такие добрые, что станут настраивать такой заголовок "впрок, дабы людям было удобно". |
Цитата:
|
Понято. Значит погуглю всевозможные варианты.
|
Часовой пояс GMT +3, время: 02:34. |