OpenSource Freeware FotoSlider on Flex 3 and jQuery.
To view this page ensure that Adobe Flash Player version 10.0.0 or greater is installed. |
Велосипедные и лыжные маршруты из Жулебино |
|
В начале 2011-го года я познакомился с платформой Flex и написал Мой первый фото-слайдер на Flex 4. В дальнейшем проект фотослайдера для компании VOTPUSK.RU стал развиваться своим путем - у него появились оооочень специфические параметры и режимы работы, кроме того у этого фотослайдера весьма специфический стиль.
Я решил сделать более универсальный фотослайдер, который бы в отличие от фотослайдера для компании вотпуск.ру
- Имел бы менее специфический стиль и более распространенный стиль - тени под фото, blur-реакция на клики, отсутствие панелей под рисунками, сами рисунки большего размера, простейшие кнопки без фирменных рисунков, небелый фон и прочее.
- При клике на рисунке слайдер вотпуска отправляет пользователя на новую вкладку браузера с хитро сформированными параметрами перехода. Для простейших применений это не нужно - фото можно показать на этой же странице.
- Слайдер вотпуска имеет сложную структуру формирования параметров для итогового клика с переходом на страничку с фото. Профессиональный программист наверное может разобраться в методике формирования URL, но остальным разбираться в этом будет невозможно. Поэтому URL рисунка большого размера в OpenSource фотосладере формируется вполне понятно - просто добавлением к параметру BaseUrl пути к рисунку.
- Слайдер вотпуска - имеет в серверной части хандлер, работающий с базой. А OpenSource фотослайдер в серверной части имеет хандлер, который просто обходит файловую систему и формирует требуемый XML для фотослайдера. Иначе говоря, OpenSource фотослайдер работает медленнее, но не требует СУБД на компьютере с фотографиями - это то, что нужно для простейшего применения фотосладера. Понятно что в слайдере для вотпуска бессмысленно ковыряться в файловой системе - там все хранится в базе, начиная от логинов, профилей пользователей, всех списков рисунков и собственно сами бинарные тела рисунков тоже лежат в SQL-сервере.
- Слайдер вотпуска предполагает загрузку в себя 20-30 рисунков сайта (которые отбирает модератор из сотен тысяч рисунков), а OpenSource фотослайдер имеет такой механизм загрузки рисунков, который не вешает браузер даже при загрузке тысяч рисунков (которые медленно-медленно выгружаются из файловой системы). В частности для примера велосипедных и лыжных маршрутов я загрузил в него около 500 фоток. Слайдер вотпуска такое сделать не может - он расчитан на очень быструю выгрузку из СУБД списка из 20-30 фоток параллельно десятками тысяч пользователей сайта.
Надеюсь, тот кто внимательно прочитал эти отличия OpenSource слайдера и слайдера Вотпуска, тот поймет, что при некоторой внешней похожести (и глобально-общем принципе построения) - у этих продуктов нет практически ничего общего в областях применения (и в нынешнем состоянии этих двух слайдров у них совпадает всего 20% программного кода слайдера). В сущности у этих слайдров общими остались только строки 146-167.
Итак, перейдем к рассмотрению кода моего OpenSource-фотослайдера:
1: <?xml version="1.0" encoding="utf-8"?>
2: <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
3: xmlns:s="library://ns.adobe.com/flex/spark"
4: xmlns:mx="library://ns.adobe.com/flex/mx"
5: applicationComplete="application1_applicationCompleteHandler(event)">
6:
7: <fx:Script>
8:
9: <![CDATA[
10: import flash.filters.DropShadowFilter;
11: import mx.collections.ArrayCollection;
12: import mx.containers.HBox;
13: import mx.controls.Alert;
14: import mx.controls.Image;
15: import mx.core.UIComponent;
16: import mx.events.FlexEvent;
17: import mx.rpc.events.ResultEvent;
18: import spark.components.BorderContainer;
19: import spark.core.NavigationUnit;
20:
21:
22: [Bindable]
23: public var BestFoto:mx.collections.ArrayCollection ;
24: public var params:Object;
25: public var ShadowFilter : DropShadowFilter ;
26: public var BgColor:String;
27: public var CurrentLoadImageNum:int
28: public var RequestTimer:Timer = new Timer(500, 100);
29:
30: protected function application1_applicationCompleteHandler(event:FlexEvent):void
31: {
32: BestFoto= new mx.collections.ArrayCollection();
33: params = mx.core.FlexGlobals.topLevelApplication.parameters;
34: if (!params.hasOwnProperty("BestFotoList")) {
35: // "http://imageslider.vb-net.com/getimagename.ashx?all=1"
36: Alert.show("flashvars parameters 'BestFotoList' not set.");
37: return;
38: }
39: if (!params.hasOwnProperty("BaseUrl")) {
40: // http://imageslider.vb-net.com/ImageSlider/
41: Alert.show("flashvars parameters 'BaseURL' not set.");
42: return;
43: }
44: if (!params.hasOwnProperty("Color")) {
45: // "#FDF5E6"
46: BgColor="#FFFFFF";
47: }
48: else{
49: BgColor=params.Color;
50: }
51: GetFoto.url=params.BestFotoList;
52: this.setStyle( "backgroundColor", BgColor)
53:
54: try {
55: ExternalInterface.addCallback("containerresize", FlashContainerResize);
56: }
57: catch (e:Error){
58: }
59:
60: ShadowFilter = ShadowEffect();
61: go_left.filters = [ ShadowFilter ];
62: go_right.filters = [ ShadowFilter ];
63:
64: try
65: {
66: GetFoto.send();
67: }
68: catch(e:Error){
69: return;
70: }
71:
72: try {
73: ExternalInterface.call("flashresize");
74: }
75: catch (e:Error){
76: }
77: }
78:
79: protected function FlashContainerResize (width1:int,heigth1:int):void {
80: HGroup1.width=width1-80;
81: }
82:
83: protected function GetFotoReadEnd(event:ResultEvent):void
84: {
85: var i:int;
86: BestFoto = event.result.BestFotoNumbers.BestFoto;
87: HGroup1.visible=false;
88:
89: if (BestFoto.list.length>10){
90: AddMyElement(0,10);
91: var BorderContainerTmp:BorderContainer = HGroup1.getElementAt(4) as BorderContainer;
92: var ImageTmp:Image= BorderContainerTmp.getElementAt(0) as Image;
93: ImageTmp.addEventListener("complete",ImageLoad_completeHandler);
94: }
95: else {
96: AddMyElement(0,BestFoto.list.length);
97: HGroup1.visible=true;
98: }
99: }
100:
101: protected function ImageLoad_completeHandler(event:Event):void
102: {
103: HGroup1.visible=true;
104: RequestTimer.start();
105: CurrentLoadImageNum=10;
106: RequestTimer.addEventListener(TimerEvent.TIMER, Timer_completeHandler);
107: }
108:
109: protected function Timer_completeHandler(event:Event):void
110: {
111: AddMyElement(CurrentLoadImageNum,CurrentLoadImageNum+10);
112: CurrentLoadImageNum+=10
113: }
114:
115: protected function AddMyElement (From:int, To:int):void {
116: var j:int;
117: for (j=From;j<To;j++) {
118: if (j<BestFoto.list.length){
119: var NewBorderContainer:BorderContainer = new BorderContainer;
120: NewBorderContainer.width = 152;
121: NewBorderContainer.height = 108;
122: NewBorderContainer.setStyle("backgroundColor", 0xD4D0C8);
123: NewBorderContainer.setStyle("borderColor", 0xD4D0C8);
124: NewBorderContainer.filters = [ ShadowFilter ];
125:
126: var NewImage:Image = new Image;
127: NewImage.width=150;
128: NewImage.height=100;
129: NewImage.maintainAspectRatio =false;
130: NewImage.addEventListener(MouseEvent.CLICK,NewImage_clickHandler);
131: NewImage.addEventListener(MouseEvent.ROLL_OVER,NewImage_MouseOverHandler);
132: NewImage.addEventListener(MouseEvent.MOUSE_OUT,NewImage_MouseOutHandler);
133: NewImage.source=params.BaseUrl + BestFoto[j].Small;
134: NewImage.data=params.BaseUrl + BestFoto[j].Big;
135: NewImage.setStyle('mouseDownEffect', GlowImage);
136: NewImage.verticalCenter=0;
137: NewImage.horizontalCenter=0;
138:
139: NewBorderContainer.addElement(NewImage);
140: HGroup1.addElement(NewBorderContainer);
141: }
142: else RequestTimer.stop();
143: }
144: }
145:
146: protected function scrollToThere(dir:uint):void {
147: var value:Number = HGroup1.getHorizontalScrollPositionDelta(dir);
148: if (value != 0) {
149: pth.valueBy = value;
150: anim.play();
151: }
152: else
153: {
154: if(dir == NavigationUnit.PAGE_RIGHT)
155: {
156: value = HGroup1.getHorizontalScrollPositionDelta(NavigationUnit.HOME);
157: pth.valueBy = value;
158: anim.play();
159: }
160: if(dir == NavigationUnit.PAGE_LEFT)
161: {
162: value = HGroup1.getHorizontalScrollPositionDelta(NavigationUnit.END);
163: pth.valueBy = value;
164: anim.play();
165: }
166: }
167: }
168:
169: protected function NewImage_clickHandler(event:MouseEvent):void
170: {
171: var Image1:Image=event.currentTarget as Image;
172: var url:String = Image1.data.toString();
173: //var request:flash.net.URLRequest = new URLRequest(url);
174: try {
175: ExternalInterface.call("requestimage",url);
176: //navigateToURL(request, "_blank");
177: }
178: catch (e:Error) {
179: return;
180: }
181: }
182:
183:
184: protected function NewImage_MouseOverHandler(event:MouseEvent):void
185: {
186: var Image1:Image=event.currentTarget as Image
187: Image1.buttonMode = true;
188: Image1.useHandCursor = true;
189: var NewBorderContainer:BorderContainer = Image1.parent.parent.parent as BorderContainer
190: NewBorderContainer.setStyle("backgroundColor", 0x3380DD);
191: NewBorderContainer.setStyle("borderColor", 0x3380DD);
192:
193: }
194:
195: protected function NewImage_MouseOutHandler(event:MouseEvent):void
196: {
197: var Image1:Image=event.currentTarget as Image
198: Image1.buttonMode = false;
199: Image1.useHandCursor = false;
200: var NewBorderContainer:BorderContainer = Image1.parent.parent.parent as BorderContainer
201: NewBorderContainer.setStyle("backgroundColor", 0xD4D0C8);
202: NewBorderContainer.setStyle("borderColor", 0xD4D0C8);
203: }
204:
205:
206: protected function go_right_mouseOverHandler(event:MouseEvent):void
207: {
208: go_right.useHandCursor = true;
209: go_right.buttonMode= true;
210: }
211:
212: protected function go_left_mouseOverHandler(event:MouseEvent):void
213: {
214: go_left.useHandCursor = true;
215: go_left.buttonMode= true;
216: }
217:
218: protected function ShadowEffect():flash.filters.DropShadowFilter
219: {
220: var filter : DropShadowFilter = new DropShadowFilter();
221: filter.blurX = 4;
222: filter.blurY = 4;
223: filter.quality = 2;
224: filter.alpha = 0.5;
225: filter.angle = 45;
226: filter.color = 0x202020;
227: filter.distance = 6;
228: filter.inner = false;
229: return filter
230: }
231:
232: ]]>
233: </fx:Script>
234:
235: <fx:Declarations>
236: <!-- Place non-visual elements (e.g., services, value objects) here -->
237: <s:HTTPService id="GetFoto" result="GetFotoReadEnd(event)" />
238:
239: <s:Animate id="anim" target="{HGroup1}" duration="500">
240: <s:motionPaths>
241: <s:SimpleMotionPath id="pth" property="horizontalScrollPosition" />
242: </s:motionPaths>
243: </s:Animate>
244:
245: <mx:Glow id="GlowImage" duration="1000" alphaFrom="0.3" alphaTo="1.0" blurXFrom="50.0" blurXTo="0.0" blurYFrom="50.0" blurYTo="0.0" color="0x3380DD"/>
246:
247: </fx:Declarations>
248:
249: <s:layout>
250: <s:HorizontalLayout gap="10" verticalAlign="middle" columnWidth="740"/>
251: </s:layout>
252:
253: <s:Button label="<" id="go_left"
254: click="scrollToThere(NavigationUnit.PAGE_LEFT);"
255: width="30" mouseOver="go_left_mouseOverHandler(event)" />
256:
257: <s:HGroup id="HGroup1" x="52" y="101" width="652" height="125" gap="20" verticalAlign="middle" variableColumnWidth="false"
258: clipAndEnableScrolling="true" columnWidth="160">
259: </s:HGroup>
260:
261: <s:Button label=">" id="go_right"
262: click="scrollToThere(NavigationUnit.PAGE_RIGHT);"
263: width="30" mouseOver="go_right_mouseOverHandler(event)"/>
264:
265: </s:Application>
Все с самого начала я описывать не буду - отсылаю вас к описанию первой прикидки слайдера вотпуска - Мой первый фото-слайдер на Flex 4 - пройдусь только по ключевым отличиям. Этот слайдер требует всего три понятных параметра, из которых один - цвет фона слайдера, а смысл двух других вполне понятен (строки 33-52). Параметр BestFotoList указывает URL хандлера, который обходит файловую систему и формирует список рисунков для отображения фотослайдером.
Такой хандлер я описал (фактически это серверная часть фотослайдера) - Как сделать простейший Web-handler формирующий XML. Второй параметр BaseUrl - это базовая часть URL (чтобы читать фотки с разнух доменов и не гонять лишнее по сети). Примеры обоих параметров вы видите в коде.
В строках 218-230 определен эффект тени, а в строке 245 фильтр Glow, который используеся для вспышки при клике на фото. Строки 146-167 анимированная прокрутка рисунков.
Основная процедура добавления рисунков 115-144. А вот цепляется она хитро - по таймеру. Сначала загружается фасад слайдера, потом реквесты выдаются по тикам таймера - чтобы не подвисал браузер и канал.
Еще одна важная фишка этого слайдера - то что он не только взаимодействует с сервером, но и активнейшим образом взаиможействует со страничкой - строки 169-181, 79-81, 72-76, 54-58. Это взаимодействие я более ли менее описывал на страничке - Как с помощью jQuery сделать флеш-ролик резиновым. Рисункок конечно показывается на страничке - чтобы он кешировался в браузере, чтобы его можно было просмотреть в полном размере, скопировать или назначить в один клик обоями рабочего стола. Как вы понимаете, показывать рисунок внутри плеера нет смысла вообще - и я назначения таких слайдров не понимаю.
Теперь перейдем к рассмотрению собственно кода странички. В основном все осмысленные операции тут выполняются jQuery:
Это исходный шаблон странички (ANT вперемешку с jQuery).
1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2: <!-- saved from url=(0014)about:internet -->
3: <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
4: <!--
5: Smart developers always View Source.
6:
7: This application was built using Adobe Flex, an open source framework
8: for building rich Internet applications that get delivered via the
9: Flash Player or to desktops via Adobe AIR.
10:
11: Learn more about Flex at http://flex.org
12: // -->
13: <head>
14: <title>${title}</title>
15: <meta name="google" value="notranslate">
16: <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
17: <!-- Include CSS to eliminate any default margins/padding and set the height of the html element and
18: the body element to 100%, because Firefox, or any Gecko based browser, interprets percentage as
19: the percentage of the height of its parent container, which has to be set explicitly. Fix for
20: Firefox 3.6 focus border issues. Initially, don't display flashContent div so it won't show
21: if JavaScript disabled.
22: -->
23: <style type="text/css" media="screen">
24: html, body { height:100%; }
25: body { margin:0; padding:0; overflow:auto; text-align:center;
26: background-color: ${bgcolor}; }
27: object:focus { outline:none; }
28: #flashContent { display:none; }
29: </style>
30: <!-- Enable Browser History by replacing useBrowserHistory tokens with two hyphens -->
31: <!-- BEGIN Browser History required section ${useBrowserHistory}>
32: <link rel="stylesheet" type="text/css" href="history/history.css" />
33: <script type="text/javascript" src="history/history.js"></script>
34: <!${useBrowserHistory} END Browser History required section -->
35: <script type="text/javascript" src="swfobject.js"></script>
36: <script type="text/javascript">
37: <!-- For version detection, set to min. required Flash Player version, or 0 (or 0.0.0), for no version detection. -->
38: var swfVersionStr = "${version_major}.${version_minor}.${version_revision}";
39: <!-- To use express install, set to playerProductInstall.swf, otherwise the empty string. -->
40: var xiSwfUrlStr = "${expressInstallSwf}";
41: var flashvars = {};
42: flashvars.BestFotoList="http://imageslider.vb-net.com/getimagename.ashx?all=1";
43: flashvars.BaseUrl="http://imageslider.vb-net.com/ImageSlider/";
44: flashvars.Color="#FDF5E6"
45: var params = {};
46: params.quality = "high";
47: params.bgcolor = "${bgcolor}";
48: params.allowscriptaccess = "always";
49: params.allowfullscreen = "true";
50: var attributes = {};
51: attributes.id = "${application}";
52: attributes.name = "${application}";
53: attributes.align = "middle";
54: swfobject.embedSWF(
55: "${swf}.swf", "flashContent",
56: "${width}", "${height}",
57: swfVersionStr, xiSwfUrlStr,
58: flashvars, params, attributes);
59: <!-- JavaScript enabled so display the flashContent div in case it is not replaced with a swf object. -->
60: swfobject.createCSS("#flashContent", "display:block;text-align:left;");
61: </script>
62: <script type="text/javascript" src="jquery-1.4.4.min.js"></script>
63: <script type="text/javascript" src="dropshadow.jquery.plugin.js"></script>
64: <script type="text/javascript">
65: $(document).ready(function () {
66: $(window).resize(flashresize);
67: });
68:
69: </script>
70: <script type="text/javascript">
71: function flashresize () {
72: var ContentContainer = $("#ContentContainer")[0];
73: var ${application} = $("#${application}")[0];
74: ${application}.containerresize(ContentContainer.clientWidth, ContentContainer.clientHeight);
75: setshadow();
76: }
77: </script>
78: <script type="text/javascript">
79: function requestimage(url) {
80: $("#bigimage")[0].src = url;
81: setshadow();
82: }
83: </script>
84:
85: <script type="text/javascript">
86: function setshadow() {
87: $('.drop_shadow_layer').remove();
88: $('.shadow').dropshadow({
89: shadowColor: '#808080',
90: shadowLayer: -100,
91: distance: '6px',
92: blur: '3px'
93: });
94: }
95: </script>
96: </head>
97: <body style="background-color:#FDF5E6">
98: <!-- SWFObject's dynamic embed method replaces this alternative HTML content with Flash content when enough
99: JavaScript and Flash plug-in support is available. The div is initially hidden so that it doesn't show
100: when JavaScript is disabled.
101: -->
102: <table cellpadding="0" cellspacing="0" border="0" style="width: 100%; height: 126px;">
103: <tr>
104: <td>
105: <a href="//www.vb-net.com/bike/" target="_blank"><span style="color: Blue; font-size: 20px;
106: text-decoration: none;">Велосипедные и лыжные маршруты из Жулебино</span></a>
107: </td>
108: </tr>
109: <tr>
110: <td id="ContentContainer" style="width: 100%; height: 126px;">
111: <div id="flashContent">
112: <p>
113: To view this page ensure that Adobe Flash Player version ${version_major}.${version_minor}.${version_revision}
114: or greater is installed.
115: </p>
116: <script type="text/javascript">
117: var pageHost = ((document.location.protocol == "https:") ? "https://" : "http://");
118: document.write("<a href='http://www.adobe.com/go/getflashplayer'><img src='"
119: + pageHost + "www.adobe.com/images/shared/download_buttons/get_flash_player.gif' alt='Get Adobe Flash player' /></a>");
120: </script>
121: </div>
122: <noscript>
123: <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="${width}" height="${height}"
124: id="${application}">
125: <param name="movie" value="${swf}.swf" />
126: <param name="quality" value="high" />
127: <param name="flashvars" value="BestFotoList='http://imageslider.vb-net.com/getimagename.ashx?all=1'&BaseUrl='http://imageslider.vb-net.com/ImageSlider/'&Color='#FDF5E6'" />
128: <param name="bgcolor" value="${bgcolor}" />
129: <param name="allowScriptAccess" value="always" />
130: <param name="allowFullScreen" value="true" />
131: <!--[if !IE]>-->
132: <object type="application/x-shockwave-flash" data="${swf}.swf" width="${width}" height="${height}">
133: <param name="quality" value="high" />
134: <param name="bgcolor" value="${bgcolor}" />
135: <param name="allowScriptAccess" value="always" />
136: <param name="allowFullScreen" value="true" />
137: <!--<![endif]-->
138: <!--[if gte IE 6]>-->
139: <p>
140: Either scripts and active content are not permitted to run or Adobe Flash Player
141: version ${version_major}.${version_minor}.${version_revision} or greater is not
142: installed.
143: </p>
144: <!--<![endif]-->
145: <a href="http://www.adobe.com/go/getflashplayer">
146: <img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif"
147: alt="Get Adobe Flash Player" />
148: </a>
149: <!--[if !IE]>-->
150: </object>
151: <!--<![endif]-->
152: </object>
153: </noscript>
154: </td>
155: </tr>
156: <tr>
157: <td>
158: <div style="height: 20px">
159: </div>
160: <img id="bigimage" style="width: 800px" class="shadow" />
161: </td>
162: </tr>
163: </table>
164: </body>
165: </html>
Итоговый шаблон получившейся (после прохода FlexBuilder) странички вы можете постмотреть здесь - //www.vb-net.com/flex3/fotoslider.html. Плагин jQuery для отрисовки тени иногда подглючивает - вам ничто не мешает его выбросить вообще.
|