Uruchomiłem nową usługę w internecie, a w zasadzie przywróciłem do życia bardzo stary projekt. Jest to monitoring stron www, którego zadaniem jest kontrola pozycji stron w wyszukiwarkach internetowych oraz kontrola działania tych stron.
Projekt już kiedyś był zrealizowany - kilka lat temu, jednak chyba wtedy było zbyt wcześnie na tego typu usługę. W zasadzie szkoda - wtedy byłem pierwszy w polskim internecie, mogłem wytrwać i mieć dzisiaj ugruntowaną pozycję. Obecnie istnieje na rynku spora konkurencja. Piszę to jako zachętę dla was do działania - masz pomysł, to go realizuj i nie poddawaj się po pierwszych trudnościach.
Należy nadpisać metodę OnPaintBackground() i samodzielnie zaprogramować rysowanie tła. Do rysowania gradientów można posłużyć się klasą LinearGradientBrush, np.:
using System.Drawing.Drawing2D;
override protected void OnPaintBackground(PaintEventArgs e)
{
LinearGradientBrush brush =
new System.Drawing.Drawing2D.LinearGradientBrush(ClientRectangle, Color.Red, Color.White, 0f);
e.Graphics.FillRectangle(brush, ClientRectangle);
}
Kto by się spodziewał, programiści frameworka .NET doszli do wniosku, że zdarzenia Click oraz DoubleClick (i pochodne) mają działać tylko po kliknięciu myszką w istniejące elementy listy. Jakie to powoduje ograniczenia? Otóż wymyśliłem sobie, że podwójne kliknięcie w puste miejsce kontrolki ListView powinno powodować utworzenie nowego elementu. Z rozpaczą jednak stwierdziłem, że to nie działa….
…zacząłem szukać w google…
…i zdruzgotany nic nie znalazłem - poza - kilkoma poradami, aby wprowadzić własną obsługę za pomocą Timera i MouseDown/MouseUp. Do bani.
A rozwiązanie istnieje i jest ono całkiem eleganckie. Należy utworzyć własną kontrolkę rozszerzającą ListView, a następnie nadpisać metodę
I już. Przechwytujemy wszystkie komunikaty i w przypadku gdy jest to podwójne kliknięcie, wywołujemy zawsze zdarzenie OnDoubleClick. Podobnie można zrobić dla pojedynczego kliknięcia, tylko sobie ustalcie ID komunikatu.
W toku jakże intensywnych prac nad efektami specjalnymi we flashu, przez przypadek stworzyłem maziageneratora - program, który także tobie pozwoli poczuć się prawdziwym impresjonistą.
Tak się kończą zabawy z Bitmap.draw, BlendMode.OVERLAY i krzywymi lissajou:
Sympatyczna strona na kampanię promocyjną firmy Komandor. I chociaż, jak to w życiu bywa, klient drastycznie zmienił naszą wstępną koncepcję, to mimo tego udało się na tyle zachować ducha strony, że przy jej pisaniu miałem odpowiednią ilość motywującego “funu” :)
Chcecie obejrzeć stronę na żywo? Klikajta w obrazek!
Metoda DownloadFile z klasy webClient przyjmuje dwa parametry: String lub URI z adresem sieciowym pliku oraz String z nazwą pod jaką plik zostanie zapisany lokalnie. Jednak ta metoda jest częściowo nieelegancka - blokuje całkowicie wątek aż do zakończenia pobierania pliku. Nie możemy w tym czasie odebrać żadnych eventów (np. użytkownik nie będzie w stanie przerwać operacji ściągania) ani np. informować o postępie. Zamiast niej możemy jednak użyć podobnej metody - DownloadFileAsync(Uri, String):
Warto zapamiętać, że za pomocą DownloadFileAsync nie możemy w jednym wątku ściągać jednocześnie więcej niż jednego pliku - w przypadku próby dokonania takiej operacji otrzymamy wyjątek. Dlatego z pobieraniem kolejnego pliku musimy poczekać do zakończenia pobierania poprzedniego.
Kolejny sympatyczny efekt, wykorzystywany bardzo często w produkcjach demosceny to tunel. W milionach różnych wersji, wyewoluował od całkiem prostej realizacji rysowanych okręgów do hiper-super-triple teksturowanego tunelu z dynamicznym oświetleniem, cieniami i bajerami.
Realizacja tego efektu we flashu jest jak najbardziej możliwa. Sprawa wygląda prosto:
Od pewnego czasu wracam wspomnieniami do czasów demosceny, a od kilku dni odświeżam stare umiejętności i przekładam to, co kiedyś robiło się w assemblerze, na Flasha i Actionscript. Jednym z wynalazków koderów z demosceny, był efekt plazmy.
Opisowo efekt ten polegał na uzyskaniu interesujących graficznie animacji “rozlewających” się kolorów. Ponieważ jednak ówczesne komputery były zbyt wolne na obliczanie co ramke wartości każdego pixela na ekranie, koderzy użyli jednej ze swoich sztuczek: tylko raz na starcie rysowało się obraz pixeli na ekranie, a potem podmieniano jedynie paletę kolorów używaną przez układ graficzny. W ten sposób nawet ZX-Spectrum dawało radę:
W takim razie, skoro ZX-Spectrum ze swoim Zilogiem Z80 dawało radę, jak poradzi sobie flash na procesorach taktowanych gigaherzami? :) Nie możemy niestety operować bezpośrednio na palecie ( zabawa z ColorTransform również odpada), więc jednak musimy za każdym razem odrysować wszystkie pixele na ekranie. Spróbujmy:
package {import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.display.StageAlign;
import flash.events.Event;
import flash.display.BitmapData;
[SWF(width="300", height="300", backgroundColor="0x000000")]publicclass PlasmaEffect extends Sprite
{privatevar w:int = 150;
privatevar h:int = 150;
privatevar colors:Array = newArray();
privatevar plasma:Array = [w];
privatevar paletteShift:int;
publicfunction PlasmaEffect(){this.stage.scaleMode = StageScaleMode.NO_SCALE;
this.stage.align = StageAlign.TOP_LEFT;
this.stage.frameRate = 10;
this.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
generatePalette();
generatePlasma();
}privatefunction draw():void{this.graphics.clear();
for(var x:int = 0; x < w; x++)for(var y:int = 0; y < h; y++){this.graphics.lineStyle(1, colors[(plasma[x][y] + paletteShift)%256]);
this.graphics.moveTo(x *2, y *2);
this.graphics.lineTo(x *2, y *2 + 1);
}}privatefunction generatePlasma():void{var colorIndex:int;
for(var x:int = 0; x < w; x++){
plasma[x] = [h];
for(var y:int = 0; y < h; y++){
colorIndex =
(128.0 + (128.0*Math.sin(x /16.0))
+ 128.0 + (128.0*Math.sin(y /32.0))
+ 128.0 + (128.0*Math.sin(Math.sqrt(((x - w /2.0)*(x - w /2.0) + (y - h /2.0)*(y - h /2.0)))/8.0))
+ 128.0 + (128.0*Math.sin(Math.sqrt((x * x + y * y))/8.0)))/4;
plasma[x][y] = colorIndex;
}}}privatefunction generatePalette():void{var r:int;
var g:int;
var b:int;
for(var x:int = 0; x <256; x++){
r = 128.0 + 128*Math.sin(3.1415* x /16.0);
g = 128.0 + 128*Math.sin(3.1415* x /128.0);
b = 0;
colors.push(r *256*256 + g *256 + b);
}}privatefunctiononEnterFrame(e:Event):void{
draw();
paletteShift += 1;
}}}
No cóż, na moim, całkiem współczesnym komputerze nie daje rady - ledwo 5 klatek na sekundę, i macierz pixeli o rozmiarach 150×150. Winowajcą jest metoda lineTo - zbyt wolna jak na nasze potrzeby.
Ale, drobna zmiana kodu, zastąpienie rysowanie poprzez graphics.lineTo na bezpośrednie ustawianie pixelów bitmapy:
package {import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.display.StageAlign;
import flash.events.Event;
import flash.display.BitmapData;
import flash.display.Bitmap;
[SWF(width="300", height="300", backgroundColor="0x000000")]publicclass PlasmaEffect extends Sprite
{privatevar w:int = 300;
privatevar h:int = 300;
privatevar colors:Array = newArray();
privatevar plasma:Array = [w];
privatevar paletteShift:int;
privatevar bitmap:BitmapData = new BitmapData(w, h, false);
publicfunction PlasmaEffect(){this.stage.scaleMode = StageScaleMode.NO_SCALE;
this.stage.align = StageAlign.TOP_LEFT;
this.stage.frameRate = 20;
this.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
this.addChild(new Bitmap(bitmap));
generatePalette();
generatePlasma();
}privatefunction draw():void{this.graphics.clear();
for(var x:int = 0; x < w; x++)for(var y:int = 0; y < h; y++){
bitmap.setPixel(x, y, colors[(plasma[x][y] + paletteShift)%256]);
//this.graphics.lineStyle(1, colors[(plasma[x][y] + paletteShift) % 256]);//this.graphics.moveTo(x * 2, y * 2);//this.graphics.lineTo(x * 2, y * 2 + 1); }}privatefunction generatePlasma():void{var colorIndex:int;
for(var x:int = 0; x < w; x++){
plasma[x] = [h];
for(var y:int = 0; y < h; y++){
colorIndex =
(128.0 + (128.0*Math.sin(x /16.0))
+ 128.0 + (128.0*Math.sin(y /32.0))
+ 128.0 + (128.0*Math.sin(Math.sqrt(((x - w /2.0)*(x - w /2.0) + (y - h /2.0)*(y - h /2.0)))/8.0))
+ 128.0 + (128.0*Math.sin(Math.sqrt((x * x + y * y))/8.0)))/4;
plasma[x][y] = colorIndex;
}}}privatefunction generatePalette():void{var r:int;
var g:int;
var b:int;
for(var x:int = 0; x <256; x++){
r = 128.0 + 128*Math.sin(3.1415* x /16.0);
g = 128.0 + 128*Math.sin(3.1415* x /128.0);
b = 0;
colors.push(r *256*256 + g *256 + b);
}}privatefunctiononEnterFrame(e:Event):void{
draw();
paletteShift += 1;
}}}
i… sukces! :) Tym razem macierz 300×300 i 20 klatek na sekundę: