Как да си направим ротатор на картинки с JavaScript

Съвсем наскоро, във връзка с един от курсовете, които посещавам в момента, ми се наложи да правя статичен уебсайт по даден дизайн. В даденото psd беше предвидено да има нещо като панел, в който се сменят различни публикации (новини с картинки и допълнителна информация) като функционалността изискваше както автоматично сменяне, така и ръчно чрез бутони напред / назад. Под въпросния панел има и част с т. нар. thumbnails, които представляват малък размер на това, което се изобразява горе. Ако се click-не на тях, автоматично картинката горе се сменя с текущата посочена. Цялото това нещо е подобно на widget и е по-известно като image rotator.
Подобни ротатори на картинки (и не само картинки, а цели панели с информация) може да откриете на сайтове като Yahoo, playlist-а на YouTube и още куп други.

В настоящият урок ще ви покажа как сами да си направите нещо подобно:

Демонстрация

Какво ще ни е нужно за целта?

  • Познания по HTML, CSS, JavaScript  (+ библиотеката jQuery).
  • Сорс код редактор, за демото съм ползвал Notepad++.
  • Някакъв опростен редактор за картинки, може да се ползва и Paint.
    Ще ви трябва за оразмеряване на картинките и crop-ване на малките thumbnails по-късно.
  • Малко свободно време, желание и готини идеи.

Какво точно ще направим и как ще изглежда?

За демонстрацията реших да направя съвсем семпъл вариант на image rotator. Вие сами може да експериментирате, да направите коренно различни неща, по-богати на съдържание, стил и функционалност. Няма да влизам в детайли относно html-а и стиловете използвани в моя вариант. Накратко имам контейнер, в който се съдържат ротатора и панелчето с малки картинки под него. Ротаторът има два бутона за навигация между картинките и средна част с големия формат на картинките (новината или статия с различно съдържание). Под тях има панелче, в което се визуализират малките картинки (thumbnails). Добавени са някои ефекти, като смяна на фона с бял при hover на мишката и оранжев при показване на текущата картинка. Накрая на тази публикация ще откриете link към целия проект и сорс код, които съм ползвал и ще може да видите самата структура и стилизиране на елементите.

По какъв начин функционира въртенето и как да го направим?

Тук идва същинската част. За по-лесно ориентиране, докато четете, може да отворите script файла от този адрес и да следите кода заедно с обясненията.

Ще започнем с променливите. Нуждаем се от един предварително деклариран масив imageURLArray, в който да се съдържат имената на файловете с големи картинки, включително разширението им. Пътят до тях няма да се специфицира тук. Останалите променливи са съответно imgHolder, която по-късно при зареждането на страницата ще открие елемента, чиято фонова картинка ще променяме, nextPictureToShow – индекс на следващата картинка за показване, refreshedIntervalId – ще я ползваме, за да стопираме и подновяваме автоматичното въртене и smallContainers, в която ще се съдържат всички елементи с малки картинки от крайния долен панел.

В моментът, в който страницата е заредена ($(document).ready(function() { … }) и всички елементи на нейното DOM дърво са правилно възприети и конструирани,
променливите ще бъдат инициализирани. Ще открием всички div-чета с клас SmallPic, който държат thumbnail-овете на големите картинки. В imgHolder ще запишем и елемента, който държи за фон главната голяма картинка на rotator-а.
Даваме начална стойност на индексатора 0 и продължаваме напред. За всеки от div-овете с малки картинки присвояваме следните event-и:
mouseover – когато курсора на мишката е над съответния елемент, към него ще се добавя клас .HoveredPic, който ще сменя фона на контейнера с бял и ще слага познатата ръчичка за курсор.
mouseout – просто ще премахва гореспоменатия клас, връщайки стиловете, както са били преди това.
click – ще извика функцията ChangeRotatorPic с параметър index сочещ към текущо извикания обект.

Ще обясня отделните методи малко по-надолу, но първо да приключим с първоначалните инициализации и извиквания при зареждане на страницата.
Останалото от тази част е закачането на event-а click за бутона наляво със съответното извикване на метода PreviousPicture() и този за надясно NextPicture().
За финал, викаме методът StartRotator(), който пуска автоматичното въртене на картинките.

ОТДЕЛНИТЕ МЕТОДИ

function StartRotator()
{
refreshedIntervalId = window.setInterval(“RotateImages()”, 3500);
}

Накратко, задавайки тази стойност на променливата, чрез методът setInterval ние заявяваме, че на всеки 3,5 секунди ще бъде извиквана функцията RotateImages().
Със съдържащите се в RotateImages() условия, въртенето ще продължава безкрайно. По-късно ще разберем как да го спрем и в кои случаи ще е нужно.
Повече за метода setInterval – тук и тук.

RotateImages()

В този основен метод правим няколко стъпки. Чрез eval() се изпълняват различни statement-и или се изчисляват изрази. Върнатите обекти могат да се ползват по-напред в работата с различни методи на чистия JavaScript или библиотеката към него jQuery. Правим проверка дали не излизаме от размера на масива с картинките. Ако това е така, връщаме стойността на nextPictureToShow на 0 (по този начин осигуряваме преминаването от последна към първа картинка). Със следващия ред
var urlToImg = “url(img/” + array[nextPictureToShow] + “)”;
създаваме променливата, която ще ползваме като стойност на свойството background на елемента, който държи главната картинка. Библиотеката jQuery предоставя изключително удобни методи за директен достъп до елемента и неговите свойства. В този случай ще работим директно върху стиловете му чрез .css(), който приема като параметрите property и value. Нагласяме някои други свойства на фоновото изображение. Премахваме предишно добавения клас .ClickedPic, а после го добавяме само за текущия елемент.
$(“.SmallPic”).eq(nextPictureToShow) обхожда всички елементи с клас .SmallPic, но връща онзи от колекцията, което е с индекс nextPictureToShow.
В jQuery, разликата между .eq(n) и .nth-child(n) е в това, че при eq n е 0-базирано, а при nth-child броенето започва от 1.
Голямата ни картинка е вече сменена, както и фона на малката, която й отговаря и стои под нея. Увеличаваме nextPictureToShow с 1 за следващата итерация.

PreviousPicture()

Този метод се вика при натискане бутона наляво за ръчна смяна на картинката (спомняте си event-а, който закачихме по-нагоре, нали?!). По-особеното е if-else частта. Ако nextPictureToShow е 0 или 1, то ние сме все още на първата картинка. Или сме заредили страницата и веднага сме натиснали назад, стойността е още 0 и не е увеличена, или RotateImages() е минало вече веднъж и стойността е 1. И в двата случая, трябва да зададем стойност на променливата равна на последния валиден индекс от масива с картинки. Така, когато сме на първата картинка и дадем назад или наляво, ще ни бъде показана последната, което е очакваното преминаване.

В противен случай, намаляме стойността на променливата за индекса с 2.
Тук идва важния момент с изпълнението на clearInterval(refreshedIntervalId);
По този начин “нулираме” таймера и прекратяваме изпълнението на RotateImages() на всеки 3 секунди и половина. Извикваме RotateImages(), за да изпълни смяната и след това стартираме отново автоматичното сменяне чрез StartRotator().

NextPicture()

работи по общо взето същия начин като предния метод. Разликата е единствено в проверката. Ако натиснем веднага след зареждане на страницата бутона за следваща снимка, може да сме изпреварили събитията с инкрементацията и смяната да зацикли. Затова, ако е 0, увеличаваме с 1, за да е всичко ОК.

ChangeRotatorPic(itemNumber)

Това е закачения за event-a click метод. Присвояваме на nextPictureToShow стойността на itemNumber, зачистваме таймера, викаме RotateImages() и накрая стартираме отново автоматичния ротатор.

Да не повярва човек, но това е всичко!

“Омръзна ми да чета, не разбрах нищо и искам код … “

Ако сте scroll-нали дотук в гнева си (ако вече не сте натиснали X-а на браузъра), ще ви разбера. Всички сме изпадали в това положение, когато думите просто не са достатъчни. Затова, ще си спазя обещанието като ви дам адрес към архивче с целия проект и всички файлове към него.

Целият сорс код и файловете към проекта

 

С този урок се опитах да ви покажа един от начините да си направим по сравнително лесен и бърз начин image rotator с възможност за автоматично и ръчно сменяне на съдържанието. Надявам се тези знания да ви послужат като основа за създаването на нещо далеч по-функционално, красиво и приложимо в бъдещите проекти, по които ще работите. Желая ви успех! : )

Харесва ли ви това? Споделете:

Unit тестване на конзолни приложения

Като част от QA академията на Телерик, наскоро ми се наложи да пиша unit тестове за едно конзолно приложение, а именно една игра. В близко бъдеще се надявам да ми остане малко повече време, за да ви представя урок как да си създадем лесно, бързо и приятно някаква old school готина игричка, но засега, да караме направо.
Ще ви споделя накратко как да подкарате компонентни тестове, които симулативно да обработват изкарване / вкарване на информация от конзолата.

Unit testing image

Какво ще тестваме?

Няма да влизам в детайли относно същността и имплементацията на възложената задача. Ще дам конкретен пример за един метод от нея. В играта имаше изискване да има табло(таблица) с резултати или т. нар. scoreboard, който да пази имената на играчите и техния резултат. Също така, scoreboard-а трябва да изкарва играчите подредени във възходящ ред по резултата (в контекста на играта, това са брой опити за решаване на проблема, т. е. по-малкият брой опити е по-добър резултат). Ако двама играчи са с еднакъв резултат, то тогава да ги подрежда по имена (азбучен ред или още лексикално сортиране). Максималният брой играчи за показване е 5, а ако таблото е празно, се изкарва определено съобщение.

За целта използвах една много полезна структура от данни, а именно OrderedMultiDictionary. За нея, авторите й и повече информация относно другите многофункционални и интересни класове, които идват с безплатното assembly, посетете тази страница.

Ще пишем на C#. Работим с Microsoft Visual Studio 2010 Ultimate и
Team Test Framework
-а към него.

Може да видите кода на моята имплементациятук
или като картинка Test code snippet oneвляво.
Това е самият метод.

Компонентно тестване на нещо, което пише по конзолата

В същността на проблема стои въпроса как най-лесно да сравним отпечатаното на конзолата с това, което очакваме и сме посочили като expected value за нашия тест. За целта ще ни е нужно да пренасочим нанякъде стандартното печатане на конзолата. Това може да се направи лесно като използваме класа StringWriter дефиниран в namespace-а System.IO (за повече информация – MSDN).

Създаване на теста в стъпки

  1. Записваме някаква sample data в нашата структура. За целта използвам самостоятелно написан метод, който добавя в речника няколко играчи.
    Може да се направи и чрез стандартно викане на метода Add на класа OrderedMultiDictionary, който ще иска да му бъде подаден като параметър обект от тип KeyValuePair<int, Competitor>.
  2. Създаваме инстанция на класа StringWriter, например:
    StringWriter sw = new StringWriter();
  3. Използваме статичния метод на конзолата Console.SetOut(TextWriter newOut) и като параметър подаваме създадената от нас инстанция на класа StringWriter, а именно sw.
  4. Викаме метода, който ще тестваме и който стандартно пише по конзолата.
    В моя случай, това е споменатия вече по-горе статичен PrintScoreBoard(). Вече пренасочен, изходът ще се запише в обекта sw.
  5. Създаваме си променлива от тип string, която да съдържа в себе си очаквания от нас резултат.
    Важно е да се отбележи, че навсякъде, където очакваме нов ред, трябва да добавим към нашия низ литерал за нов ред. В примерчетата отдолу съм го направил използвайки placeholder и property-то NewLine на класа Environment, който универсално работи за всяка среда, под която се компилира приложението, добавяйки правилния литерал.
  6. Използваме Assert.AreEqual, сравнявайки обекта sw обърнат до string със създадената от нас променлива.
  7. Пускаме теста и ако всичко е наред, трябва да е минал успешно.

Test results image

Може да видите кода на моята имплементациятук
или като картинка вляво.Test code snippet two
Това са два теста на метода PrintScoreBoard().

 

 

 

 

Ами ако желаем да симулираме вкарване на информация от конзолата?

Това вече е малко по-абстрактно, но не толкова сложно. Използва се друг клас – StringReader (MSDN). Накратко казано, изходът отново се пренасочва към StringWriter, а входът към инстанция на StringReader.
Ще ви дам конкретен, лесен пример.

Snippet one imageSnippet two image
Това е написаният от нас метод и теста към него (двете картинки отгоре).

Питаме потребителя как се казва, четем отговора му и после отпечатваме поздрав.
Тестът работи по следния начин:

  1. Пренасочваме стандартния изход на конзолата към обект от тип StringWriter.
  2. Създаваме инстанция на класа StringReader и като параметър подаваме всичко, което очакваме от потребителя. В нашия случай, това е име последвано от нов ред (тъй като стандартно се вика Console.ReadLine()).
  3. Пренасочваме входа на конзолата към нашия обект от тип StringReader.
  4. Създваме си променлива от тип string, в която вписваме очаквания резултат. Важно е да се отбележи, че това ще е само изходната информация. Прочитането вече е извършено и ние очакваме резултат на базата на него.
    Внимавайте също и с местата, на които се очаква да бъде отпечатан нов ред.
    Често това води до грешки и пропадане на теста.
  5. Сравняваме очакваното с обекта от тип StringWriter обърнат до string.

Ако всичко е наред, тестът минава успешно.

Писането на компонентни тестове е важно за осигуряване качеството на нашата разработка без значение дали тя е desktop, web или просто конзолно приложение. Надявам се този урок да е в помощ на онези от вас, които са се сблъскали със задачата да изтестват метод, който стандартно изкарва нещо на конзолата или пък чете от нея. Желая ви успех! : )

Харесва ли ви това? Споделете: