Back-End

25 abr, 2007

Montando um sistema de tickers com Flash + XML + PHP + MySQL

Publicidade

Olá amigos e amigas! Para quem não me conhece, sou um dos moderadores do Fórum de Flash do iMasters e é com imenso prazer que publico meu primeiro artigo aqui no portal. Vamos lá!

Esse artigo abranje duas áreas distintas que se interligam pelo XML. A primeira delas será entre o Flash e o XML, a montagem final dos tickers. Depois explicarei como mudar esse XML dinamicamente através do cadastro em BD.

Veja nesse link uma prévia de como ficará o nosso sistema.

Segue agora a primeira parte:

Relacionando o Flash com XML

Esse artigo é de nível intermediário, pois alguns conceitos de Flash e de programação são exigidos.

Vamos ver agora como ler os dados do xml e transformar as informações em aplicações práticas.

1. Criando o arquivo xml antes de tudo.

CODE

<?xml version="1.0" encoding="utf-8"?>
<tickers>
  <ticker>
    <titulo>Uma vista</titulo>
    <area>Montanhas</area>
    <texto>Table Mountain and Cape Town</texto>
    <foto>foto1.jpg</foto>
    <link>http://www.ponha-o-link-que-quiser.com.br</link>
  </ticker>
  <ticker>
    <titulo>Manobra Radical</titulo>
    <area>Esportes</area>
    <texto>Inline Skater on Vertical Rise of Ramp</texto>
    <foto>foto2.jpg</foto>
    <link>http://www.ponha-o-link-que-quiser.com.br</link>
  </ticker>
  <ticker>
    <titulo>Com grande angular</titulo>
    <area>Cidade</area>
    <texto>View of Ibiza Town</texto>
    <foto>foto3.jpg</foto>
    <link>http://www.ponha-o-link-que-quiser.com.br</link>
  </ticker>
</tickers> 

Providenciei um .zip com o .xml e as fotos para ficar mais fácil.

Reparem na estrutura do arquivo. Ele tem uma tag que cobre todas as outras e tags menores (<ticker>), cada uma com os dados de uma notícia, foto, ou qualquer coisa para que você utilize o sistema.

2. Agora nós vamos preparar a interface no Flash. Abra um novo documento e adicione:

  • Um botão com setinhas para a esquerda chamado “ant_btn”.
  • Um botão com setinhas para a direita chamado “prox_btn”. Pode ser o outro botão espelhado. Arraste uma cópia dele e vá em Modify > Transform > Flip Horizontal. Recomendo isso porque economiza bytes.
  • Um mc grandão que ocupará a área das fotos chamado “area_mc”.

Misture bem até que se tenha uma massa homogênea e reserve. Dê uma olhada:

2.1. Crie um quadradinho, que servirá de botão para o menu, e transforme-o num mc de nome de instância “menu”. Dentro dele crie uma nova layer e coloque um botão invisível (com conteúdo apenas do frame HIT) do tamanho do quadradinho e chame-o de “botao”. Numa layer abaixo coloque um Dynamic Text com o nome “numero_txt”. Adicione esse mc que você acabou de criar no layout do flash.

2.2. Desenhe um retângulo da largura das fotos (360 px no caso) e transforme-o num mc chamado “legenda_mc”. Agora entre no legenda_mc e crie uma layer. Nela, crie dois Dynamic Texts chamando-os de “titulo_txt” e “texto_txt”. Lembre-se de ativar os caracteres que for usar em Emded. Para ficar mais claro dê uma olhada na screen:

Agora posicione o legenda_mc no palco.

2.3. Por último, crie uma camada acima da legenda e desenhe um quadrado que ocupará a área do legenda_mc, depois transforme esse quadrado num movieclip chamando-o de “mascara_mc”. Coloque o tipo da layer como Mask. Adicione também uma layer acima de todas as outras, chamada Actions. O resultado final deverá ser esse:

3. Enquanto o layout fica fermentando, vamos avançar na parte mais temida: a programação! Vou fazer aos poucos pra irmos entendendo a lógica do código.

3.1. Captando os dados do xml, importando classes e já ajustando os botões vai e volta. Na timeline principal, na layer Actions, adicione:

CODE

//Importando as classes para fazer os movimentos dinâmicos
import mx.transitions.*; 
import mx.transitions.easing.*; 
 
//Criando o xml e fazendo ele ignorar espaços em branco 
var tickersXML:XML = new XML(); 
tickersXML.ignoreWhite = true; 
 
//Quando o xml carregar com sucesso executar as funções 
tickersXML.onLoad = function(success:Boolean):Void  { 
 
    //(o xml é carregado na última linha do script) 
    if (success) { 
 
        //Número total de tickers 
        n_tickers = tickersXML.firstChild.childNodes.length; 
 
        /*O ticker_atual será o último pois ao executar a função de 
        proximoTicker() ele passará automaticamente para o 1º resultado*/ 
        ticker_atual = n_tickers; 
 
        //Acertando as funções dos botões 
        prox_btn.onRelease = function():Void { 
 
            //Se o fade das fotos tiver terminado 
            if (fotoIn.position == fotoIn.finish) { 
 
                //Vai para o próximo ticker  
                proximoTicker(); 
            } 
        }; 
 
        ant_btn.onRelease = function():Void { 
            if (fotoIn.position == fotoIn.finish) { 
                anteriorTicker();  
            } 
        }; 
 
        //Chamando o primeiro ticker 
        proximoTicker(); 
 
       /*******************************************************
              Códigos do passo 3.2 serão adicionados aqui 
        *******************************************************/
 
    } else { 
 
        //Caso dê errado o carregamento, avise 
        sobeLegenda(); 
        legenda_mc.titulo_txt.text = "Ocorreu um erro"; 
        legenda_mc.texto_txt.text = "Por favor entre em contato com a administração"; 
    } 
}; 

/******************************************************* 
  Códigos do passo 3.3 em diante serão adicionados aqui 
*******************************************************/ 
 
//Carregando o XML e dando início ao processo 
tickersXML.load("tickers.xml");

Vamos às considerações sobre o AS até agora. Primeiro a gente cria o objeto XML e a função que será executada quando carregar. Nela vem a parte “se tudo der certo” e depois “se acontecer algum erro”. Repare que nessa segunda parte existe uma função chamada sobeLegenda() que ainda não criamos, mas já dá pra ter a idéia de o que ela faz, certo?

Depois de carregarmos o XML contamos quantos tickers temos. A idéia é:

tickersXMLtodo o documento|

firstChildo primeiro nó do xml (<tickers>) que contém todas as informações|

childNodesuma array que contém todos os nós com as informações (<ticker>)|

lengtho tamanho da array, ou seja, quantos ticker temos

Se ficar alguma dúvida faça testes com o trace(), e sempre! Por exemplo: trace (tickersXML.firstChild); Veja a diferença do resultado do trace quando vai se adicionando as palavras-chave. É muito importante que você compreenda o que cada comando faz ao ler o XML.

Vejamos agora a função do prox_btn.onRelease. Ela é inteira dentro de um if, que verifica se o fade das fotos já terminou (para não dar mais de um fade ao mesmo tempo e dar pau). Se tiver terminado (o fade) ele passa para o próximo ticker. A mesma coisa é para o botão ant_btn, porém com a função de voltar um ticker.

Depois de programados os botões, o script chama o primeiro ticker.

3.2. Montando o menu para o internauta ir direto para algum dos tickers. Substitua o comentário do código anterior por:

CODE

//Criando o menu duplicando o quadradinho existente 
//Deixando o quadradinho base invisível 
menu._visible = false; 
 
//Pegando a posição inicial _x do menu 
var menu_x = menu._x; 
 
//Definindo a distância entre os botões 
var menu_dist = 25; 
 
//Criando um laço que gerará os botões do menu. Ele executa uma vez para cada ticker. 
for (var i:Number = 0; i < n_tickers; i++) { 
 
    //Duplicando o movieclip menu 
    //O depth é i+5 porque até o depth 4 existem mcs ocupando [fotomc1, fotomc2, legenda_mc, area_mc] 
    var menubtn:MovieClip = menu.duplicateMovieClip("menu" + i, i + 5); 
 
    //Movendo o botão duplicado no eixo x. 
    menubtn._x = i * menu_dist + menu_x; 
 
    //Número do ticker 
    menubtn.numero = i; 
 
    //Texto do botãozinho 
    menubtn.numero_txt.text = i + 1; 
 
    //Quando apertar o botão, mudar para ticker indicado 
    menubtn.botao.onRelease = function():Void { 
 
        //Se o fade tiver terminado 
        if (fotoIn.position == fotoIn.finish) { 
 
            vaiParaTicker(this._parent.numero);  
        } 
    }; 
}  

Dentro do laço for são criados cópias do botãozinho e são adicionadas as propriedades deles. Primeiro a gente move o botão para onde queremos. Funciona assim: da primeira vez que o for é executado a variavel i vale 0.

Então o primeiro botãozinho será colocado na posição x =(0*25+58)=58 (supondo que menu_x seja 58).

O segundo será colocado na posição x=(1*25+58)=83 e assim por diante.

Cada mc recebe uma variável (numero) com o número do ticker que ele tem que chamar. Recebe também o texto que é colocado no DynamicText. O botão invisível funciona de modo parecido com o prox_btn. A diferença é que usa a função que vai direto para um ticker. Obs: o this._parent.numero pega a variável numero do pai do botão invisível, ou seja, o mc menu.

3.3. As funções de avanço e retrocesso. Agora vamos finalmente criar as funções proximoTicker(), anteriorTicker() e vaiParaTicker(numero_ticker). Substitua o comentário do primeiro código por:

CODE

//Função que passa para o próximo ticker 
function proximoTicker():Void { 
 
    //Se o ticker atual não for o último, aumenta em 1, se for ele vai para 0 
    ticker_atual < n_tickers - 1 ? ticker_atual++ : ticker_atual = 0; 
 
    insereDados(ticker_atual); 
 
    //Limpando o intervalo de troca de tickers 
    clearInterval(ticker_intervalo); 
} 
 
//Função que passa para o ticker anterior 
function anteriorTicker():Void { 
 
    //Mesmo esquema da outra função 
    ticker_atual > 0 ? ticker_atual-- : ticker_atual = n_tickers - 1; 
 
    insereDados(ticker_atual); 
} 
 
//Função que vai direto para um ticker, é o mesmo esquema das outras 
function vaiParaTicker(numero_ticker:Number):Void { 
 
    ticker_atual = numero_ticker; 
 
    insereDados(ticker_atual); 
} 
 
//Função que insere os dados vindo do XML no nosso sitema 
function insereDados(ticker_atual:Number):Void { 
 
    //Colocando titulo e texto na legenda 
    legenda_mc.titulo_txt.text = tickersXML.firstChild.childNodes[ticker_atual].childNodes[1].firstChild.nodeValue + " > " + tickersXML.firstChild.childNodes[ticker_atual].childNodes[0].firstChild.nodeValue; 
 
    legenda_mc.texto_txt.text = tickersXML.firstChild.childNodes[ticker_atual].childNodes[2].firstChild.nodeValue; 
 
    //Carregando a foto 
    carregaFoto(tickersXML.firstChild.childNodes[ticker_atual].childNodes[3].firstChild.nodeValue); 
 
    //Definindo o link para quando clicar na foto 
    area_mc.onRelease = function():Void { 
        getURL(tickersXML.firstChild.childNodes[ticker_atual].childNodes[4].firstChild.nodeValue, "_self"); 
    }; 
} 

Vamos entender como funciona a primeira linha do próximoTicker(). Ela é na verdade um if(){}else{} reduzido. Veja ele “desenvolvido”:

CODE

//Desenvolvido 
if(ticker_atual < n_tickers - 1) { 
    ticker_atual++; 
} else { 
    ticker_atual = 0; 
} 
 
//Reduzido 
ticker_atual < n_tickers - 1 ? ticker_atual++ : ticker_atual = 0;

Isso pode ser visto apenas como um capricho, mas acho interessante mostrar essa outra forma para que reconhecam-a caso vejam em outro código. As demais linhas da função seguem colocando os textos na legenda, o link no onRelease da area_mc e chamando a função de carregar a foto, a próxima que veremos. Para ir esclarecendo as dúvidas em relação ao endereçamento das informações no xml, use e abuse do trace(). Coloque só a primeira palavra-chave e depois vá testando adicionando as demais. Assim ficará mais fácil ter clareza de como o ActionScript trabalha.

As duas outras funções são bem parecidas com a primeira, exeto pela manipulação da variável ticker_atual.

Uma observação em relação a primeira função: o clearInterval é para limpar o intervalo que foi criado na função de adicionar a foto no palco. Isso garante que só depois que a foto foi carregada é que o sistema começa a contar o tempo para carregar a próxima foto.

3.4. Carregando as fotos. Essa parte é um pouco complicada, tem que sacar como funciona o esquema de dois movieclips para carregar as fotos e dar os fades. Adicione esse código embaixo do código do passo 3.3.

CODE

//Criando os dois movieclips para carregar as fotos 
this.createEmptyMovieClip("fotomc1", 2); 
this.createEmptyMovieClip("fotomc2", 1); 
 
//Posicionando os mcs no palco
fotomc1._x = fotomc2._x = area_mc._x; 
fotomc1._y = fotomc2._y = area_mc._y; 
 
//Definindo qual está em cima (por causa dos fades) 
fotoCima = fotomc1; 
 
//Colocando a legenda por cima das fotos
legenda_mc.swapDepths(3); 
 
//Função que carrega a foto e cria os fades 
function carregaFoto(scr:String):Void { 
 
    //Parando o fade da foto anterior caso ainda esteja ocorrendo 
    fotoIn.stop(); 
 
    //Criando o mcloader 
    var mclFoto:MovieClipLoader = new MovieClipLoader(); 
 
    //Carregando a foto 
    mclFoto.loadClip(scr, fotoCima); 
 
    //Quando terminar de carregar a foto 
    this.onLoadInit = function():Void  { 
 
        //Chamar o próximo ticker 
        clearInterval(ticker_intervalo); 
        ticker_intervalo = setInterval(proximoTicker, 5000); 
 
        //Fazer o fadeIn com a foto de cima 
        fotoIn = new Tween(fotoCima, "_alpha", None.easeNone, 0, 100, 1, true); 
 
        //Quando terminar a transição de fotos 
        fotoIn.onMotionFinished = function():Void  { 
 
            //Pegar a foto de cima e jogar para trás 
            if (fotoCima == fotomc1) { 
 
                //Tirando o clip do fotomc2 que irá para frente 
                mclFoto.unloadClip(fotomc2); 
                fotomc1.swapDepths(fotomc2); 
 
                //Ao ir pra frente estará preparado para receber outra foto 
                fotoCima = fotomc2; 
            } else { 
                //Mesma coisa, caso seja o fotomc2 que estiver em cima 
                mclFoto.unloadClip(fotomc1); 
                fotomc2.swapDepths(fotomc1); 
                fotoCima = fotomc1; 
            } 
        }; 
    }; 
    //Adicionando o listener ao mcloader 
    mclFoto.addListener(this); 
}  

Inicialmente criamos os mcs nos níveis 1 e 2, e colocamos a legenda por cima deles, no nível 4. Dentro da função nós criamos uma instância do MovieClipLoader. Dê uma lida nesse tutorial para saber mais. Após isso mandamos o MCL carregar a foto e definimos que quando o carregamento terminar o fade deve ser executado. O fade é feito através da classe Tween, tem um tutorial que explica essa classe bem detalhadamente também.

Quando terminar o carregamento da foto, repare que chamamos a função proximoTicker depois de um intervalo de 5 segundos (5000 milisegundos). Esse número pode ser trocado sem problemas.

Quando o fade termina nós temos a seguinte situação:

  • um movieclip por cima com a foto recém-carregada
  • um movieclip por baixo com a foto antiga

Dependendo de qual estiver por cima, nós descarregamos a foto do mc que estiver atrás e trocamos os dois de lugar, assim a gente terá um mc vazio pronto para receber outra foto e dar o fade.

3.5. Finalizando o código com as funções da legenda. Vamos criar agora as funções de sobeLegenda() e desceLegenda(), além de configurar estados padrão e o aparecimento da legenda.

CODE

//Fazendo com que a legenda suba quando o mouse estiver em cima da foto 
area_mc.onRollOver = sobeLegenda; 
area_mc.onRollOut = desceLegenda; 
area_mc._alpha = 0; 
area_mc.swapDepths(4); 
 
//Pegando as posições da legenda 
var leg_aparece = legenda_mc._y; 
var leg_esconde = legenda_mc._y + legenda_mc._height + 1; 
 
//Função que esconde a legenda com Tweens 
function desceLegenda():Void { 
    var desceTween:Tween = new Tween(legenda_mc, "_y", Strong.easeOut, legenda_mc._y, leg_esconde, 1.5, true); 
} 
 
//Função que mostra a legenda com Tweens 
function sobeLegenda():Void { 
    var sobeTween:Tween = new Tween(legenda_mc, "_y", Strong.easeOut, legenda_mc._y, leg_aparece, 1.5, true); 
} 
 
//Abaixando a legenda por padrão 
legenda_mc._y = leg_esconde; 
 
//Configurando a máscara da legenda 
legenda_mc.setMask(mascara_mc); 

Não tem muito segredo aqui. Apenas definimos quando a legenda aparece e como. Estude a classe Tween do Flash que você poderá aperfeiçoar e personalizar tanto o movimento da legenda quanto o aparecimento das fotos. É só trocar os parâmetros na hora de criar o Tween, o resto continua configurado. Prático, não?

Salve seu aquivo e dê um Ctrl+Enter para gerar e swf. Certifique-se de que o swf, as fotos e o xml estejam na mesma pasta. Agora está finalizada a parte de Flash! Pode testar que você verá seu lindo sisteminha já funcionando!

Observações sobre práticas de organização do AS. Esse pode não ser o exemplo mais (com o perdão da expressão) “webstandard” de Flash. Mas algumas coisas podem ser ressaltadas:

  • Colocar sufixos nos nomes de instâncias (_mc, _txt, _btn). Além de facilitar a identificação de o que se trata, o flash lista automaticamente os métodos, eventos e propriedades desse objeto ajudando na hora de escrever seu script.
  • Comentar o código. O comentário não serve só pra fazer tutoriais, serve para te orientar caso volte a trabalhar com o projeto e para orientar colegas de trabalho, se você trabalhar em equipes.
  • Usar nomes coerentes nas funções. Ajudam você a entender o código mais fácilmente (como o sobeLegenda, no começo da explicação do tuto).
  • Usar uma variável para o número total no for, exemplo:

CODE

//Adequado 
n_tickers = tickersXML.firstChild.childNodes.length; 
for (var i:Number = 0; i < n_tickers; i++) {} 
 
//Não adequado 
for (var i:Number = 0; i < tickersXML.firstChild.childNodes.length; i++) {} 

Isso aumenta o processamento desnecessariamente.

  • Usar uma variável para se referir a um mc dinamicamente:

CODE

//Recomendado 
var menubtn:MovieClip = menu.duplicateMovieClip("menu" + i, i + 5); 
menubtn._x = i * 25 + 58; 
 
//Não recomendado 
menu.duplicateMovieClip("menu" + i, i + 5); 
_root["menu"+i]._x = i * 25 + 58;  

Pela mesma razão de carregar o processador desnecessariamente.

Bom galera, espero que tenham curtido essa primeira parte do tutorial e que o aproveitem para muitas coisas, inclusive para criar uma galeria de fotos! Se quiser é só tirar a parte de chamar a função proximoTicker() automaticamente que você terá uma ótima opção para mostrar seu portifolio!

Mas ainda não acabou. Vamos ver agora como gerar o xml por PHP pegando dados do MySQL.

Gerando o XML com PHP e MySQL

Esse tutorial é de nível básico-intermediário, pois alguns conceitos de PHP e de MySQL são exigidos.

Bom galera, vamos começar criando e populando a nossa tabela. Baixe as instruções em SQL para ficar mais fácil.

Depois de ter a tabela configurada, vamos criar o arquivo PHP que gerará o xml:

CODE

<? 
  //conectando ao mysql 
  $conn = @mysql_connect("localhost", "SEU LOGIN","SUA SENHA");   
  $db   = @mysql_select_db("SEU BD", $conn);  
 
  //Selecionando todos os registros da tabela tickers 
  $sql = "SELECT * FROM 'tickers' ORDER BY 'id' ASC"; 
 
  //Executando a instrução sql 
  $sql  = mysql_query($sql); 
 
  //Pegando o numero de registros 
  $rst = mysql_num_rows($sql); 
 
  //Se tiver algum registro 
  if($rst > 0) { 
 
    // Abre / cria o arquivo tickers.xml com permissão para escrever 
    $xml = fopen("tickers.xml", "w"); 
 
    //Escreve o cabeçalho e o primeiro nó do xml 
    fwrite($xml, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"); 
    fwrite($xml, "<tickers>\r\n"); 
 
    //Para cada registro que tiver 
    for($i=0; $i<$rst; $i++) { 
 
      //Pegamos o valor de cada registro 
      $titulo = utf8_encode(mysql_result($sql,$i,"titulo")); 
      $area = utf8_encode(mysql_result($sql,$i,"area")); 
      $texto = utf8_encode(mysql_result($sql,$i,"texto")); 
      $foto = utf8_encode(mysql_result($sql,$i,"foto")); 
      $link = utf8_encode(mysql_result($sql,$i,"link")); 
 
         //Guardamos na variavel $conteudo as tags e os valores do xml 
         $conteudo = "<ticker>\r\n"; 
         $conteudo .= "<titulo>$titulo</titulo>\r\n"; 
         $conteudo .= "<area>$area</area>\r\n"; 
         $conteudo .= "<texto>$texto</texto>\r\n"; 
         $conteudo .= "<foto>$foto</foto>\r\n"; 
         $conteudo .= "<link>$link</link>\r\n"; 
         $conteudo .= "</ticker>\r\n"; 
 
      //Escrevendo no tickers.xml 
      fwrite($xml, $conteudo); 
    } 
 
    //Finalizando com a última tag 
    fwrite($xml, "</tickers>"); 
     
    //Fechando o arquivo 
    fclose($xml);     
 
  } 
?>  

Salve como xml.php e teste (lembre-se que a pasta precisa de permissão para gravação). Pronto, só isso. Depois você pode criar algum sistema para dar um update do MySQL e logo após chamar esse aquivo php para gerar novamente o XML. Mas não vou explicar isso aqui pois foge do escopo desse artigo.

Bom pessoal, agora está finalizado o artigo! Espero que tenham gostado e que aproveitem bastante.

Abraço, até a próxima!