Scaling and animating CSS sprites

I’m working on a project called SnappyEverAfter. It’s a wedding photo sharing with mobile apps thing. Like disposable cameras. But with smartphones.

While working on this I had to deal with some tricky CSS bits, and I’m so proud with one particular trick that I want to share it there.

So, the task: on the website, I’ll have a grid of thumbnails, the thumbnails would have hover effect, and respond to clicks and drags.

OK, I’ll just start with UL and float its LI’s:

Inside each LI there would be IMG, centered using margins. At first my hover effect was just a border, later I added scale effect, and even animated it, because it’s just so easy to do with CSS3:

I’m omitting vendor prefixes and other stuff here for clarity. I added some rounded corners and drop shadows too and this is how my thumbnails looked, with one of them zoomed in:

Sweet, all done! Except, uh, … “transition: sclale” thing was making my Chromium crawl. Don’t know why. Anyway, instead of that, we can do the zoom effect by changing size and margins (and animate that too!):

Chromium was quick again, Firefox quick as ever, IE9 OK too, but no border or shadow cuteness in IE8! Also, with 20 separate thumbnails I was making 20 HTTP requests, not optimal.

At first, I did get IE8 stuff working with CSS PIE, but it’s a hack… There’s some flicker, and it interferes with my drag&drop. I guess, because it is sensitive to z-indexes. And still the 20 requests nastiness…

It was time for CSS sprites! CSS sprites is a technique of putting many small images in single big picture, so they download at once. At display time use “background-position” CSS rule to shift the big picture around. That’s the traditional approach. And I can pre-render rounded borders and drop shadows in the sprite image!

My problem with traditional approach is that one cannot get background to scale. There’s “background-size” CSS rule but that’s not well-supported. “transform: scale” is out because of performance problem. One interesting idea I encountered was to put icons in webfont. Clever, right, but only works for monochrome vector data. But then there’s an idea of using DIV and IMG inside. DIV would be “overlay: hidden” so it would work as a mask, IMG would be absolutely positioned so its correct region is seen through DIV:

See, IMG is like a filmstrip, and DIV lets you see only one frame of it. On hover, I adjust both vertical offset for IMG, and size of the DIV. And I animate both. Well it kind of works in Chrome and Firefox, a bit wiggley but works. I assume it is wiggley because there are several properties independently animated, these are in floating point values, and rounding errors cause things to jump forth and back a bit. In Chromium, I got an artifact with frames not lining up during animation. Again, the problem seemed to be with independently animating several properties and rounding problems.

Also, IE9 appears to be totally uncool about relatively positioned elements in large quantities. It gets slow, very slow.  OK, so here’s my fix to both problems:

First, IMG is offset with negative margin, not “top” rule. So DIV doesn’t need to be relatively positioned and IE is happy.

Second, offset is given as percentage. This way, I don’t need to animate offset, and the problems with animating several things at once go away. Looks nice and sweet on Chrome, Chromium and Firefox. Looks correct with shadows and all on IE too, no animation there though. And all thumbnails load in one request. And it’s fast, and fluid. It’s beautiful!

That’s it, now I’ll go back and hover over thumbnails and stare at the effect some more 🙂

CSN jautājumi 1.1

New version up! I’ll share some technical notes here.

Mobile web dev, PhoneGap, jQuery Mobile and XUI

One feature that got requested was to include a reference of driving rules within the app. The reference is available on internet (i.e. I don’t have to scan books), as a long formatted HTML document. Formatting is bold, italic, font sizes, superscripts. For the mobile, it makes sense to keep format in HTML and use WebView to display it. But one long page is not easy to get around. So…

First, got python script running that splits the page in subpages, it uses both BeautifulSoup and regexps, whichever is easier in each case. It generates output with Mako templates.

Next, bundle the static HTML, JS and CSS in the app with PhoneGap. But, umm, phonegap.jar is big (158KB although ProGuard would probably shave some parts off). And phonegap.js is big as well at 146KB. I’m developing for mobile, I’m concerned with file sizes, kilobytes count! Scratch that, bytes count!

So I got rid of phonegap, as I didn’t really have use all the cool integration features, I just needed a WebView that loads local static content.

Next I stole some generic style rules from jQuery Mobile. Next I integrated the whole jQuery Mobile, which drags in jQuery itself too. Just to rip them out soon after. Because, again, for the little features that I needed (showing/hiding elements, handling onclick) this was definitely an overkill, dragging along IE compatibility stuff and everything. Next I picked up xui.js. Mostly because I was amused with the name and its meaning in Russian, but also because on the homepage it was described as being “super micro tiny”. At 10KB, I thought, it wasn’t all that super-micro-tiny. But it’s OK, and the library packs a (relatively) lot in those 10KB.

Yeah, and browsers these days. I’ve been out of the loop for some time, and look now, animations in CSS, gradients in CSS, nice nice nice.

The other things

Manipulating messy HTML and getting the taste of mobile web development was really the most interesting part, but there were few others.

Did the translation to Russian. Russian texts on average seem to be longer than Latvian so had to do some conditional font size tweaking. Also, they take twice the space on disk, because each character is two bytes in UTF-8! sqlite file with Latvian questions is 270KB, Russian version is 542KB for approximately the same number of questions. When compressed the difference goes down to about 30%.

I’ve fixed some bugs (one was to do with picture sizing on XHDPI devices like Galaxy Nexus. I want Galaxy Nexus. I want many things.), and am aware of some bugs still remaining.

In closing, an unrelated comment about the Go language! I haven’t written a single line of Go yet, but from the samples I’ve seen and articles I’ve read it looks like a sweet language. Low-level enough to be able to do interesting things fast–think data structures, pointers (I’m obsessed with optimizations if you haven’t noticed by now), and still feel reasonably high-level, readable and concise. I don’t have an immediate application for it, I however have a small hope that in the next IO Google annouces Go as supported on Android and releases SDK and everyone gets crazy writing impressively performant apps.

Hello Django

After long time with Android, I’m doing some part-time web development again, this time with Django. Being Django noob, I’m “OMG” about the vastness of the ecosystem around it. Some pointers and gotchas I’ve encountered in past couple days:

  • Django Best Practices document, and linked resources, e.g. Django Coding Style. This lead me to
    • Rearranging and renaming my templates
    • Splitting up my urls.py file
    • Getting I18N to work early. LOCALE_PATHS must be set in settings.py!
    • Moving my stuff into an virtualenv

    Also reassured some things I had done were right to do:

    • Start populating DEVELOP.txt and README.txt early on
    • Install requirements with pip from requirements.txt. My app doesn’t have setup.py yet, and it isn’t managed by Silver Lining, I’ll think about deployment options when it’s closer to being somewhat ready
  • There was a bit of mess with static files. I’m using django.contrib.staticfiles, and my STATIC_ROOT and STATICFILES_DIRS variables are set like here (except I have “whatever” instead of “bla” in STATIC_ROOT and this path doesn’t have to exist by the way).
  • I’m playing with django-cms. Some discoveries:
    • CMS toolbar didn’t show up in my templates, turns out had to add {{ cms_toolbar }} tag in templates.
    • South, the intelligent schema and data migrations thing seems nice. But it broke my tests, I had to add SOUTH_TESTS_MIGRATE = False in settings.py to fix this
    • Sekizai is a neat useful library. It’s used in django-cms for handling css and js imports in templates
  • When I run tests with ./manage.py test it appears all the included tests for Django and all of the things in my INSTALLED_APPS are run. Signifcant portion of them fail, and that’s apparently normal. For example, Sekizai ships with tests but with no templates that the tests use–in my setup these tests have no chance to succeed. So apparently I should only test my stuff, like so: ./manage.py test my_cool_app my_other_cool_app ...
  • I’m developing in Eclipse, with PyDev plugin. PyDev has special project type “Django project”. With that set up, I can run manage.py commands and development server from within Eclipse. That’s cool, but I’m so used to console…
  • Overall I’m impressed by readability of the code. When facing a problem, it’s sometimes more productive to view .py source than search on internets
  • That’s it for now, but there’s lot more to come, because, LOOK AT THIS

LockingHorizontalScrollView update

First, good news everyone, Google has released ViewPager widget. Check it out!

I’ve done some small tweaks to my LockingHorizontalScrollView as well:

  • Fixed the bug where short and slow scroll wouldn’t register as fling
  • Added getPosition() and setPosition(int position) calls that let you get/set “screen” currently visible
  • Added setInsensitiveEdges(boolean value) method. After calling setInsensitiveEdges(true) the touch events close to left and right sides of widget won’t be considered when detecting scrolls and flings. This works around the problem when user has hand wrapped around phone and touches sides of touchscreen by accident, and the widget jumps!
  • Updated example application and .apk file.
  • New demo video!

Code lives here in bitbucket.

***

Yeeeah, and so, having finished Deus Ex 3 late yesterday, I’m back to normal life and work.

Those guards sure got confused when I was sprinting through them with invisibility cloak on. Likewise, dynamic languages sure can confuse hell out of poor unsuspecting programmer: quick recap of last 3 hours of adventures:

  • Let’s set up development environment for my Pylons app on Fedora 15, Python2.7! Deployment of this app is managed by SilverLining, which has strong ties with Ubuntu and Py2.6–so I’m in unexplored teritory, exciting!
  • Replace Py 2.6 references in SilverLining to 2.7
  • Install my app: some weird errors mentioning sys.prefix but app seems to work overall
  • Let’s run tests: nose complains that there’s no such thing as “--with-pylons” flag
  • That’s because I cannot do “import pylons
  • That’s because I cannot do “import paste.registry
  • That’s because for some reason Paste didn’t get installed in lib/python but in lib/python2.7/site-packages
  • Research virtualenv
  • Research sitecustomize.py
  • Where does sys.prefix come from?
  • Dig through easy_install, setuptools
  • Dig through distutils, discover distutils.cfg, discover Distribution.parse_config_files(). Smell magic
  • Discover setuptools.dist.Distribution but no magic there
  • Discover code in distutils/__init__.py that patches Distribution class at runtime (this is the confusing dynamic bit)
  • Discover code in SilverLining that patches distutils/__init__.py
  • Finally understand what’s going on and can fix the sys.prefix issue
  • Try tests again?

May 3 – May 4

  • Want motorcycle licence
  • Do online trial exams many times to beat exam questions into head (exam UI looks like this)
  • Mobile broadband is slow, want stuff to work offline
  • Do a bunch of exams with Chrome Developer Tools on side
  • Write a python script that does exams and harvests data (in action looks like this)
  • Convert harvested data in one long HTML page, that’s useful but not fun (looks like this)
  • Get an idea to make an Android app
  • Picture illustrations take 50 megabytes
  • Try PNG, GIF, JPG with some downsizing, palette reduction etc.
  • Picture illustrations take 10-20 megabytes anyway
  • Try WebP, pictures fit in 3 megabytes
  • Sleep
  • Package all questions, answers and illustrations in one sqlite file
  • On Android cannot use sqlite file from /res/raw
  • Copy sqlite file to SD card on first startup, use it from there
  • Android cannot decode WebP out-of-the-box
  • Learn about Android NDK
  • Learn about JNI
  • Write JNI wrapper code, compile libwebp with NDK
  • Pictures come from sqlite BLOBs, decodes with native code, show up correctly on screen, very happy
  • Make launcher icon in Android Asset Studio
  • Publish on market
  • Blog about it