Categories
Technology

Demonstrating Photoshop Functionality with X-Objects

Copy found at Wayback Machine Archive.

I upgraded my original cross-browser DHTML objects into the new X-Objects — with support for Mozilla and Navigator 6.0.

To try out the new objects, I migrated 4 DHTML demos into using the new objects. These demos demonstrate different aspects of using Adobe PhotoShop’s photo filters and editors. I’ll be going over each demo in this article, but first, I wanted to describe another set of JavaScript objects used in the examples: the Animators.

Animator scripting objects

When I first started working with DHTML, and creating DHTML effects, I used to manually code animations within the page where the animation displayed. These animations used a JS timer created using the function setTimeout.

For instance, my first DHTML animation was about an animated menu that didn’t do much more than move pieces of the menu around the Web page (see animated menu). This example has changed based on new versions of both IE and Navigator, but for the most part the animation starts once the page is loaded, and continues until the last animation step is reached:

function MoveObjects() {
   theobjs["menu1"].objMoveAbsolute(
		TimingObjectsX[0][currentTick], 
                TimingObjectsY[0][currentTick])
   theobjs["menu2"].objMoveAbsolute(
		TimingObjectsX[1][currentTick], 
                TimingObjectsY[1][currentTick])
   theobjs["menu3"].objMoveAbsolute(
		TimingObjectsX[2][currentTick], 
                TimingObjectsY[2][currentTick])
   theobjs["menu4"].objMoveAbsolute(
		TimingObjectsX[3][currentTick], 
                TimingObjectsY[3][currentTick])
   theobjs["menu5"].objMoveAbsolute(
		TimingObjectsX[4][currentTick], 
                TimingObjectsY[4][currentTick])
   theobjs["menu6"].objMoveAbsolute(
		TimingObjectsX[5][currentTick], 
                TimingObjectsY[5][currentTick])


   currentTick++
   if (currentTick < 8) 
      setTimeout("MoveObjects()", 400)
}

I’ve also used setInterval as well as setTimeout to create repeating timers as well as non-repeating ones.

After a while, I started creating more complex animations such as DHTML demonstrations of how a hurricane forms, or the Dr. Dotty games, covered in a previous InterActZone article. One thing I found out is that the amount of code to control multiple objects in a synchronized animation was enormous and I got a bit tired of coding and re-coding the same timer synchronization efforts.

So, I created what I call the Animator objects. This set of objects provides for most major types of DHTML animation — movement, clipping, visibility, ordering, and sliding (combination of movement and clipping) — and has built in animation synchronization. So, you can have an animation sequence that moves 3 DIV blocks around the page, while a fourth block is being clipped and a fifth block is being hidden.

You can take a look at the Animator script file as I go through how the objects work.

How Animators work

There are three types of Animation objects used to build an animation.

First, an Animation Sequence is created. An Animation Sequence is a container for several different animator objects, all of which are animated as a unit. To create the Sequence object, use the following:

      seq = new animatorSequence();

Once the Animator Sequence is created, then Animators are added. An Animator adds a specific animation effect over a specified period of time, and over a specified number of steps. So, if I want an animation to last 100 microseconds, and occur over 15 steps, I would create the following Animator:

      var anim = seq.newAnimator(15,100);

I can add more than one Animator to a sequence, and each Animator is played in turn, as they are added to the sequence.

Once the Animator object is created, Animations can be added to the object. For instance, to move two different Web page DIV blocks at the same time, I would use Animator code similar to the following:

anim.addAnimator(theobjs["box1"],"M",160,320);
anim.addAnimator(theobjs["image2"],"M",-240,320);

In this, two DIV elements, named “box1” and “image2”, are moved at the same time, so they’re added to the same Animator. Each Animation is defined by the animation type — move type is defined with a letter “M” — and any characteristics of the animation, such as the end location of the move.

I could at this point add more Animator objects (there really is no limit to the number of Animators added to an Animation Sequence, other than limits within the underlying JS, such as array elements and number of timers). I could also add more Animations to the current Animator.

When I’m finished defining the existing Animation Sequence, I can then play it:

    seq.play();

What happens is that the animation starts, beginning with the first Animator added to the sequence, and continuing, in order, until all of the Animators have been played. Additionally, when an Animator is associated with more than one HTML element, the DHTML effect assigned is applied to each HTML element, in turn, in a synchronized manner over all of the steps of the animation.

The types of animations currently implemented are:

  • Clipping: addAnimator(theobj, “C”, newtop, newleft, newbottom, newright)
  • Movement: addAnimator(theobj, “M”, newtop_position, newleft_position)
  • Visibility: addAnimator(thobj, “H”) //Hide, use “S” for show
  • Display: addAnimator(theobj, “D”, display_type) // “block” or “none” for display
  • Z-Order: addAnimator(theobj, “Z”, z-order)
  • Replacement: addAnimator(theobj, “R”, replacement_string) // based on innerHTML
  • Parm_Replace: addAnimator(theobj, “P”, tag, classname, id, contents) // parameterized replacement
  • Slider: addAnimator(theobjs, “SL”, sliding_value, sliding_direction)

The Slider has a type of “SL”, but anything will do — it is the default animation. The first parameter is the type of slider (top to bottom, left to right, right to left, or bottom to top), and the direction specifies which way the slide will move. Note: The Slider has not been converted to working with Mozilla/Navigator 6.0 and may actually be deactivated from the Animators at some point.

Next up, we’ll use both the Animators and the X-Objects to create the PhotoShop demos.

Correcting a Photo

The first PhotoShop DHTML demo is based on using PhotoShop’s various tools and techniques to correct a bad photo. This demo is the simplest of the PhotoShop animations — using CSS absolute positioning and visibility, as you can see by accessing the Example page and picking the first demo.

To create the DHTML effects, the X-Objects and Animators are added to the page first:

<!-- cross-browser objects -->
<SCRIPT; src="cbobjects.js" language="javascript" 
type="text/javascript">>
</SCRIPT>

<!-- animation and sequence objects -->
<SCRIPT; src="animator.js" language="javascript" 
type="text/javascript">
</SCRIPT>

The content of the page is added, next. To ensure that the page’s DHTML effects work with Navigator 4.x as well as Mozilla/Navigator 6.0 and IE, all animated content is enclosed in CSS absolutely positioned DIV blocks:

<DIV; id="top" style="position:absolute; left: 20; top: 50; 
       width: 600;visibility:hidden">
Unfortunately, most photos do not turn out as we would like,
 or at least as much as I would like. Usually I'll re-take
 the photo, but sometimes this isn't feasible, or I like
 most aspects of the photo and hate to 
lose the shot. Enter Photoshop to the rescue. 
</DIV>

Once the content has been added, code is added to handle user actions. As the demo is opened into its own browser window, to close this window, the user can press the Space Bar. Additionally, hitting the ENTER key will cause the PhotoShop demo to proceed to the next “slide” in the DHMTL effect. As both actions are based on keyboard events, the keyboard events are captured and assigned to a event handler function:

   // capture events
   if (navigator.appName == "Microsoft Internet Explorer") {
	wdth = document.body.clientWidth;
	}
   else if (navigator.appName == "Netscape"){
	wdth = window.innerWidth;
   	document.captureEvents(Event.KEYPRESS);
      }
   else
      document.addEventListener("keyup",keypress,true);


   // assign function to event handler
  if (navigator.appName != "Mozilla")  
    document.onkeypress=keypress;

The event handler function pulls the keyCode from the event, and tests to see if the space bar or the ENTER key has been hit. If the space bar is hit, the page is closed. If the ENTER key is hit, the next slide is played if the demo is not at the last slide already :

function keypress(e) {
   if (!canbeginnow) return;

   if (navigator.appName == "Microsoft Internet Explorer") 
      tmp = window.event.keyCode;
   else if (navigator.appName == "Navigator")
	tmp = e.which;
   else if (navigator.appName == "Mozilla")
       tmp = e.keyCode;
   
   // if space bar hit
   if (tmp == 32) {
	window.close();
	return;
	}
   if (tmp == 13) {
      if (slide == MAX_SLIDES) 
	   return;
      next_slide();
      }
}

The code for the demon’s Animation Sequence is added next. There’s only one used for this PhotoShop demo — the animation played at the end of the demo. Its Sequence and Animators are created and played within one function:

// create animation sequence
function do_animation() { 

      // create new sequence
      seq = new animatorSequence();

      // add animator objects to sequence
      var anim = seq.newAnimator(20,100);

      // add animators
      anim.addAnimator(theobjs["image1"],"M", 200, 20, 0, 0);
      anim.addAnimator(theobjs["image2"],"M",-214,-300,0,0);
      anim.addAnimator(theobjs["image3"],"M",-214,500,0,0);
      anim.addAnimator(theobjs["image4"],"M",200,340,0,0);

      anim = seq.newAnimator(1,100);
      anim.addAnimator(theobjs["top"], "H", 0,0,0,0);
      anim.addAnimator(theobjs["top2"], "S", 0,0,0,0);
 
      anim = seq.newAnimator(20,100);
      anim.addAnimator(theobjs["image1"], "M", 150, 20, 0, 0);
      anim.addAnimator(theobjs["image4"], "M", 150,325,0,0);
     
      
      // play sequence
      seq.play();
}

There is only one Animation Sequence created, and contains 3 separate Animators. The first moves Animator is played over 100 ms, and consists of 20 steps. In this Animator, two of the demo page’s images are moved “off screen” (beyond page boundaries), and the remaining 2 are lined up next to each other. The second Animator consists of 1 step: hiding current text at top of demo page, and showing new text. The last Animator again is played out over 100 ms, and 20 steps, and this animation again moves the two images that are still showing on the page. After the Animation Sequence is created, and the three animators are added, the sequence is immediately played.

Finally, the code to handle each demo “slide” is added:

// play next presentation slide
// until last slide
function next_slide() {

   // return if animation is playing, or past last slide
   if (animatorPlaying) return;

   if (slide == 5) {
	alert("That's the end of the demonstration. 
               Re-load page to run again.");
	return;
      }

   theobjs["text" + slide].objHide();
   slide++;
   if (slide <= 4) {
    theobjs["image" + slide].objShow();
    theobjs["text" + slide].objShow();
    }
   else if (slide == 5) { 
    theobjs["text4"].objHide();
    do_animation();
    }	
}

Each “slide” consists of an image and text that are hidden together, or shown together. Notice that after the last slide is shown (slide 5) that the animation is then run.

Again, try out Demo1 from the the Example page. After clicking through all of the slides, you’ll trigger the animation. Note that it plays three separate animations, each after the other.

The next PhotoShop animation uses the tools layering ability to create a double image.

Creating a Double Image

The concept of “layers” existed in graphics, and particularly in PhotoShop long before they appeared in DHTML. An image can consist of more than one layer, with each layer having different content, color, what have you, but all of the layers combined forming the image.

A fun use of Adobe PhotoShop layers is to create a double image, and the second PhotoShop DHTML demo presents this, as you can see for yourself by accessing Demo 2 from the Example page.

 

Again, as with Demo1, the Animator and X-Object JS files are added to the page, and event handling is added. This doesn’t change from the previous example, so the code is ommitted.

In this new demo, there are several animation sequences, which are created individually as part of an array:

// setup animations
function animationSetup() { 

seq[0] = new animatorSequence(); 

var anim = seq[0].newAnimator(1,100);
anim.addAnimator(theobjs["top"],"H");
anim.addAnimator(theobjs["top1"],"S");
anim.addAnimator(theobjs["box1"],"M",160,wdth);
anim.addAnimator(theobjs["box1"],"S");

anim = seq[0].newAnimator(20,100);
anim.addAnimator(theobjs["box1"],"M",160,320);
anim.addAnimator(theobjs["image2"],"M",-240,320);
  
seq[1] = new animatorSequence();

var anim = seq[1].newAnimator(1,100);
anim.addAnimator(theobjs["top1"],"H");
anim.addAnimator(theobjs["top2"],"S");

anim = seq[1].newAnimator(5,110);
anim.addAnimator(theobjs["image1"],"M",160,145);

anim = seq[1].newAnimator(10,110);
anim.addAnimator(theobjs["image1"],"M",160,320);
anim.addAnimator(theobjs["image3"], "C",0,0,193,248);

anim = seq[1].newAnimator(1,100);
anim.addAnimator(theobjs["image1"], "H");
anim.addAnimator(theobjs["box1"], "H");

seq[2] = new animatorSequence();

anim = seq[2].newAnimator(1,100);
anim.addAnimator(theobjs["image2"],"M",160,-260);
anim.addAnimator(theobjs["image2"],"S");
anim.addAnimator(theobjs["top2"],"H");
anim.addAnimator(theobjs["top3"],"S");

anim = seq[2].newAnimator(20,100);
anim.addAnimator(theobjs["image2"], "M",160 ,60);

seq[3] = new animatorSequence();

var anim = seq[3].newAnimator(1,100);
anim.addAnimator(theobjs["top3"],"H");
anim.addAnimator(theobjs["top4"],"S");

anim = seq[3].newAnimator(5,100);
anim.addAnimator(theobjs["image3"],"M",160,310);

anim = seq[3].newAnimator(20,100);
anim.addAnimator(theobjs["image3"],"M",160,60);
anim.addAnimator(theobjs["image4"],"C",0,0,214,250);

anim = seq[3].newAnimator(1,100);
anim.addAnimator(theobjs["image3"], "H");
anim.addAnimator(theobjs["image1"], "H");

seq[4] = new animatorSequence();

var anim = seq[4].newAnimator(1,100);
anim.addAnimator(theobjs["top4"],"H");
anim.addAnimator(theobjs["top5"],"S");

anim = seq[4].newAnimator(10,100);
anim.addAnimator(theobjs["image4"],"M",110,180);
    
anim = seq[4].newAnimator(1, 100);
anim.addAnimator(theobjs["image2"], "H");
anim.addAnimator(theobjs["image3"], "H");
anim.addAnimator(theobjs["image4"],"H");
anim.addAnimator(theobjs["image5"],"S");
}

As you can see, there are 5 different animation sequences defined, for the 5 different slides of the DHTML demo. By using an array, only one sequence is played at a time.

This demo uses clipping. At this time, clipping as defined for IE 4.x and 5.x as well as Navigator 4.x, has a different frame of reference than that for Mozilla/Navigator 6.x. Because of this, in my current examples I test for the type of browser and use different clipping values based on the findings within the X-Objects (read more about clipping in X-Objects: Clipping).

Playing each slide of this demo actually plays each Animation Sequence as it is defined within the animation array:

// play next presentation slide
function next_slide() {
   if (animatorPlaying) return;
   if (slide > 4) {
	alert("That's the end of the demonstration. 
               Re-load page to run again.");
	return;
      }
   seq[slide].play();
   slide++;

}

Again, try out Demo2 from the the Example page.

Cropping a Photo

The third Adobe PhotoShop DHTML demo demonstrates how to use cropping within this tool. The DHTML used is primarily clipping with some movement, as well as hiding and showing content. I won’t repeat the code here, but you can see Demo3 yourself from the Example page. To see the demo code, choose View…Source from the browser menu.

Using the Rubberstamp tool to correct a background

The third Adobe PhotoShop DHTML demo demonstrates how to use cropping within this tool. The DHTML used is primarily clipping with some movement, as well as hiding and showing content. Again, I won’t repeat the code here, but you can see Demo4 yourself from the Example page. To see the demo code, choose View…Source from the browser menu.

Categories
People Political

Bali/Australian help

In addition to donations to help Chris’ friend, Rick, you can also help the Australian victims of the Bali explosions by donating to the Australian Red Cross. This same money will also be used to help with recovery efforts in Bali itself.

I know there was an issue with the Red Cross in regards to their handling of monies for the WTC victims in this country, primarily because the Red Cross uses whatever money isn’t needed for the aid of the victims for future disasters. This isn’t stealing from those in need and giving to the rich — this is smart fiscal management. And as you can see in the Australian Red Cross page, they very carefully note that any unused monies go to help in future disasters.

And, unfortunately, the money will most likely be needed in the future. Our world is changing.

Categories
Diversity Weblogging

Everything to do with her being a woman

Recovered from the Wayback Machine.

The comments that made me so angry yesterday were attached to a posting Doc Searls wrote.

It would seem that a weblogger who works for Microsoft was being parodied, and not in good, gentle fun, either. This bit of school yard bullying was further compounded by an article by Andrew Orlowski in the Guardian. He wrote:

Of course, you’ll argue: we’re just being mean. Online journals give a billion people who can’t write and who have nothing to say the means to publish. It’s good!

To which I reply: here’s a mechanism which allows a billion people who can’t sing, can’t write a song or make an original beep, and have nothing to express, the means to deafen me with their tuneless, boring cacophony.

There is nothing I dislike more than some elitist who thinks to him or herself, “I am hot shit”, and proceeds to prove it by dumping the cold, icy water of disdain and disparagement on anyone that might, just might, prove that what they really are is a wet match rather than a blazing torch.

You could compare the parody and the original weblog. You could, except Beth Goza took down her weblog.

Doc defended Beth, a move that wasn’t easy because he knows both Beth and Andrew. I admire him for taking a stand as a professional journalist taking another professional journalist to account for using his position within a professional publication to attack what is nothing more than a personal weblog. Sure Beth may have talked about her job at Microsoft, but most of us talk about our jobs. And our cats. And the TV shows we watched last night. No call for this behavior. None at all.

However, lest you think I was so angry because of the article and the parody, I was a bit, but not enough to send me out of the house. What really made me angry was the following, written by Dave Winer in the comments:

Why such a chivalrous defense of Beth?
What did Orlowski say that was so terrible?

Does it or did it matter to you that this is about a woman?

As one who gets it every day, I gotta say it’s not cool that Doc stands up for her, when the criticism was so mild, and when she used blogs for her marketing work at MS. For crying out loud, where is the offense?

Where does sex enter into this? If we’re only allowed to defend those of the same sex, or our defense of another human being is questioned because of their sex, then this medium is truly become a sexist one, in the worst way.

I responded with any angry comment yesterday, one that Doc rightfully called me on.

Today, when I was calmer, I wrote the following:

Sorry, Doc. You’re right — this posting wasn’t meant to be objective, and my anger got away with me in my original response.

Defending another person is a noble thing to do. But bringing that person’s sex into this, out of the blue, was totally out of line. As I tried to say in my original email (and was angry, as you can tell by my use of Sullivan), if we’re going to introduce ‘sex’ into discussions such as these, then this medium has become a sexist medium.

I don’t know this person and I wanted to defend her, and not because of her sex. It was because she had a personal weblog where she talked about her company, true, but where she also talked about her cat, and her thoughts, and just stuff. A weblog. And this so-called journalist invoked an extreme elitist attitude and made fun of her, in the worst school yard bully manner. And there is nothing I despise more than a bully.

Your defense was appropriate if you think he was being mean, and personal. There was never an issue of sex in this.

By introducing Beth’s sex, Dave demoted this issue to one of ‘boys and girls’, and that was wrong, very wrong. To Beth, to Andrew, to you, to your readers.

If we introduce sex into this story, then could we also say that if Beth hadn’t been a woman, the parody site wouldn’t have been created? Or could we say if Beth hadn’t been a woman, Andrew wouldn’t have written the article, or been so cutting?

But there was no evidence that Beth’s sex had anything to do with the parody site, or Andrew’s article. So why introduce her sex when it came to her defense?

And because the issue of sex was raised, will you be more hesitant to defend a woman in the future? Will you question your motives for defending a woman? Will you ask yourself, “Am I doing this because the person deserves my defense? Or am I doing it because she’s a woman?”

I saw your defense as a professional journalist taking another to account for attacking what is nothing more than a personal journal. Sex had nothing to do with it. Well, not until it was introduced, and then it shadowed everything that occurred before and after.

We all have strength enough to fight our own battles. But if we all do so alone, then what’s the point of reaching out, with connecting with each other? When we defend another, we’re not helping just the person we’re defending — we’re helping ourselves.

Sorry for comments, and comment length. I think you were right to defend Beth and I think well of you for the act. And I agree, I hope she does get another weblog, and continues writing exactly like she does.

Dave responded immediately with a comment that said, I have to quote from memory, that Doc’s response had “everything to do with her being a woman”, and that I was out of line, and owed him a retraction and an apology for my statement yesterday. I would quote it, but the comment was pulled. Instead, a new one was added containing the following*:

Shelley, thank god you’re not the final arbiter of right and wrong.

Further, I went to the trouble of talking with Doc and asking questions and listening to the answers. We’ve been friends for fifteen years. For you to presume you know what’s right and wrong between Doc and myself is the height of arrogance.

Why don’t you ask some women what they think, if you’re full of it or not. The comments you make about me, here and elsewhere are so off the wall. I was going to demand an apology, but changed my mind. No one takes you seriously Shelley, you might want to check that out. You’ve got a few syncophants who post in your comments, but people cut you a wide path because you’re so abusive and so unfair in your criticism. I can tell you I do that, and I’ve heard it from a bunch of other people. For what it’s worth.

I do owe an apology, but it was to Doc for flaming him yesterday. That was wrong, and uncalled for, and I apologize. And I do owe Doc and Dave an apology for questioning Doc’s objectivity when it comes to their friendship and communication with each other. That was out of line.

As for the rest:

Dave, this isn’t a school yard, but I recognize another bully when I see one. The playground may be bigger, and you may be using a keyboard instead of dirt and fists, but you’re still a bully. You call people names and then cry ‘foul’ when they respond. You demand courtesy and give none. You expect fair play, and then hit below the belt. You have power, and you’re not afraid to use it to hurt others. You say the nastiest things and then you delete them after the damage has been done. When people take you to account for outrageous statements, you start clutching your chest and say, “I’m still a sick man”.

Out of curiosity, I went to your weblog, Dave, and used Google to search on “sorry” and “apologize” within your weblog postings. What an interesting experience. Have you ever apologized for anything you’ve said?

Dave, you don’t have to worry about any of my ‘sycophants’ defending me, me being a woman and all. I can handle my own battles with the likes of you. And I won’t fight my battles by lurking in others comments, either.

*Note: the comments I quoted of Dave’s have been edited. Again.

Categories
Connecting

Hands

Chris’ friend Rick is hurt quite badly from the Bali explosion. He has burns over 45% of his body, and has received injuries to his head, lungs, and other organs.

Rick could be in intensive care in Australia for a couple of months before being allowed home to Canada. Once he returns home, he’ll need to have additional treatment, considerable treatment. An added difficulty is that Rick is far away from his family and friends, who have to fly back and forth to be with him, most likely having to stay in hotels when they visit him.

I can’t stop Bush wanting to wage war, and I can’t stop terrorists from wanting to blow up innocent people, and I can’t stop people wanting to kill each other, but there is one thing I can do:

[removed]

Categories
Weblogging

Gently walk the deer in my mind

Earlier today I was angry. Stomping around angry. The kind of anger that sends your cats and your kids for cover. I was angry because of comments attached to a posting at another weblog. Foolish comments. Hateful comments. Generating the type of anger that sends you out of your chair, causes you to yell at your computer, makes your head ache. You know the kind of anger I’m talking about.

I tried to write, a new weblog posting or to the RDF book, but couldn’t focus because of the anger. Finally, I gave up and went for a walk at Powder Valley. Sometimes a brisk walk works where all else fails.

No one else was around as I stomped along the trail, disregarding everything around me, lost in my anger, in the words that created my anger. I ignored the squirrels and the chipmunks and the wind through the trees and the crickets and the sounds of the creek and the wonderful smell and feel of fall. No room for all of that when one is consumed by anger.

And then, just as the trail climbed past a stand of trees, there they were. Five deer not more than 20 feet in front of me on the side of the trail. I stopped dead. They stopped dead. We just looked at each other in surprise. When I continued to hold still, the younger deer resumed eating and the herd began to slowly make its way past me, ever so close.

I looked into the eyes of the oldest deer, the one that seemed to be watching over the herd. They were the eyes of a being completely and utterly at home within its world. For all of humanity’s surperiority, for all of our art and music and writing and intelligence and culture, few of us will ever have that look in our eyes.

I spend too much time reacting to people who make me angry and not enough time to those who make me smile. That’s why I’ll never have that look in my eyes.