Skip to main content

Using purgetss.ui in Titanium Classic

Titanium Classic uses the same runtime module as Alloy, but it does not compile Alloy XML, TSS, utility classes, controllers, or $.* references. Generate the module and configure it with JavaScript objects containing native Titanium properties.

Quick start

From the Classic project root:

purgetss module

This creates Resources/lib/purgetss.ui.js. A minimal Resources/app.js can then load all commonly used exports:

Resources/app.js
const {
createAnimation,
Appearance,
deviceInfo,
saveComponent
} = require('lib/purgetss.ui')

Appearance.init()

const window = Ti.UI.createWindow({ backgroundColor: 'surfaceColor' })
const card = Ti.UI.createView({
width: 220,
height: 120,
borderRadius: 16,
backgroundColor: 'accentColor',
opacity: 0,
transform: Ti.UI.createMatrix2D().scale(0.92)
})
const label = Ti.UI.createLabel({ text: 'Tap to animate', color: 'textColor' })
const cardMotion = createAnimation({
duration: 220,
curve: Ti.UI.ANIMATION_CURVE_EASE_OUT,
animationProperties: {
open: { opacity: 1, scale: 1 },
close: { opacity: 0, scale: 0.92 }
}
})

function onCardClick() { cardMotion.play(card) }
function onWindowOpen() { cardMotion.open(card) }
function onWindowClose() {
card.removeEventListener('click', onCardClick)
window.removeEventListener('open', onWindowOpen)
window.removeEventListener('close', onWindowClose)
}

card.add(label)
card.addEventListener('click', onCardClick)
window.add(card)
window.addEventListener('open', onWindowOpen)
window.addEventListener('close', onWindowClose)
window.open()

// Optional diagnostics; this logs values and returns undefined.
// deviceInfo()

// saveComponent() is intentionally not called here because it writes a PNG
// and invokes the system photo-gallery API. See "Runtime utilities" below.

The semantic names in this example come from Resources/semantic.colors.json; see Appearance.

Public exports

ExportType and returnNotes
AnimationPropertiesConstructor; returns the decorated animation viewPublic, but createAnimation() is clearer in Classic code
createAnimation(args)Factory; returns the decorated animation viewPreferred Classic entry point
deviceInfo()Function; returns undefinedLogs platform and display data
saveComponent({ source, directory? })Function; returns undefinedWrites a PNG and invokes the gallery API
AppearanceSingleton objectExposes init(), set(), get(), and toggle()

An animation instance is a zero-size, touch-disabled Ti.UI.View decorated with the methods below. Treat it as a behavior object; it does not need to be added to the window.

Alloy and Classic matrix

FunctionalityPublic APIAlloy exampleTitanium ClassicPlatform note or limitation
Toggle a stateplay, toggle<Animation> plus $.*animation.play(view)Both names run the same function
Apply immediatelyapplyController referenceanimation.apply(view)No native animation is started
Force a stateopen, closeopen: and close: classesanimationProperties.open/closeDoes not toggle the requested state
DragdraggableClasses plus controllerDirect view referencesRegister only after views are in the hierarchy
Remove dragundraggableController cleanupanimation.undraggable(view)Required lifecycle cleanup
CollisionsdetectCollisionsAlloy IDsDirect view referencesTests the dragged view's center point
Serial motionsequenceArray of $.* viewsArray of Titanium viewsWaits between views; play(array) runs in parallel
Exchange positionsswapTwo $.* viewsTwo Titanium viewsDepends on rendered rect values
Attention pulsepulseView referenceanimation.pulse(view, count)Forces autoreverse and ease-in-out
Error feedbackshakeView referenceanimation.shake(view, intensity)Uses six short phases
Nearest targetsnapToView and target IDsDirect view referencesReturns the selected target or null
Index mappingreorderViews and mappingViews and mappingMapping length must match view count
Layout presettransitionViews and layoutsViews and object layoutsMissing layouts hide and disable a view
ThemeAppearanceSemantic classesSemantic names in native propertiesInitialize before the first window
DiagnosticsdeviceInfoCommonJS importCommonJS importLogs only
SnapshotsaveComponentCommonJS importCommonJS importWrites a file and invokes the gallery

Animation method reference

MethodSignatureReturn or callback
playplay(viewOrViews, callback?)undefined; callback once per view
toggletoggle(viewOrViews, callback?)Same function and result as play
applyapply(viewOrViews, callback?)undefined; synchronous callback once per view
openopen(viewOrViews, callback?)undefined; callback once per view
closeclose(viewOrViews, callback?)undefined; callback once per view
draggabledraggable(viewOrViews)undefined
undraggableundraggable(viewOrViews)undefined
detectCollisionsdetectCollisions(views, dragCallback?, dropCallback?)undefined
sequencesequence(viewOrViews, callback?)undefined; callback once after the final view
swapswap(view1, view2)undefined
pulsepulse(view, count = 1)undefined
shakeshake(view, intensity = 10)undefined
snapTosnapTo(view, targets)Selected target, null, or undefined for a missing source
reorderreorder(views, newOrder)undefined
transitiontransition(viewOrViews, layouts)undefined

The state-method callback contains type, bubbles, cancelBubble, action, state, id, targetId, index, total, and getTarget(). detectCollisions() uses different callbacks: the hover callback receives (source, targetOrNull), while the drop callback receives (source, target) only when a target was found.

Native animation objects

Use native Ti.UI.Animation properties directly:

Resources/app.js
const { createAnimation } = require('lib/purgetss.ui')

const motion = createAnimation({
duration: 240,
delay: 40,
curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT,
repeat: 1,
autoreverse: false,
top: 24,
left: 32,
width: 180,
height: 96,
opacity: 0.8,
backgroundColor: '#2563eb',
scale: 1.05,
rotate: 4
})

Top-level scale, rotate, and anchorPoint values are converted to a Ti.UI.Matrix2D. The same conversion is made for open and close states. Consult Titanium's Animation and Matrix2D references for the native property contracts.

Anchor points are platform-specific

Titanium documents anchorPoint on Ti.UI.Animation for Android and on Ti.UI.View for iOS. Test pivot-dependent motion on both platforms. In Classic, set the view's iOS anchor point before animating and pass the Android animation anchor point only inside a platform guard.

Resources/app.js
const { createAnimation } = require('lib/purgetss.ui')

const pivot = { x: 0, y: 0.5 }
const properties = { duration: 220, rotate: 12 }
const isIOS = ['iphone', 'ipad'].includes(Ti.Platform.osname)

if (isIOS) card.anchorPoint = pivot
if (Ti.Platform.osname === 'android') properties.anchorPoint = pivot

const pivotMotion = createAnimation(properties)

States and child animations

Resources/app.js
const { createAnimation } = require('lib/purgetss.ui')

const panelMotion = createAnimation({
duration: 220,
animationProperties: {
open: { opacity: 1, scale: 1 },
close: { opacity: 0, scale: 0.94 },
complete: { borderColor: '#22c55e' },
children: { duration: 160 }
}
})

titleLabel.animationProperties = {
child: { delay: 40 },
open: { opacity: 1, top: 16 },
close: { opacity: 0, top: 24 },
complete: { color: '#22c55e' }
}

panelMotion.open(panel)

Child properties are merged in this order: the animation's children object, the child's child object, and finally the child's active open, close, or complete object.

Drag and drop objects

Resources/app.js
const { createAnimation } = require('lib/purgetss.ui')

const dragMotion = createAnimation({
duration: 160,
bounds: { top: 12, right: 12, bottom: 12, left: 12 },
draggingType: 'animate',
draggable: {
drag: { opacity: 0.7, scale: 1.04 },
drop: { opacity: 1, scale: 1 }
},
animationProperties: {
keepZIndex: true,
snap: { back: true, center: true }
}
})

piece.bounds = { bottom: 40 }
piece.constraint = 'horizontal'
piece.draggingType = 'apply'
piece.draggable = {
drag: { opacity: 0.5 },
drop: { opacity: 1 }
}

dragMotion.draggable(piece)
dragMotion.detectCollisions([piece, target], (source, hovered) => {
target.borderColor = hovered === target ? '#22c55e' : 'transparent'
}, (source, droppedOn) => {
Ti.API.info(`${source.id} dropped on ${droppedOn.id}`)
})

view.bounds overrides individual values from the animation's bounds. Constraints are read from view.constraint, while draggingType and draggable.drag/drop can be global or per-view. The module forwards every property supplied in drag and drop; it does not filter size, transform, or anchor properties.

keepZIndex prevents promotion on touch start. It does not preserve the values that existed before draggable([view1, view2]): array registration assigns each view its array index. Register views one at a time if their initial z-index values must remain unchanged.

Collision detection uses the dragged view's center point. rect and coordinate conversion require the views to be attached and laid out, so initialize the playground after open or postlayout.

Lifecycle and cleanup

Resources/app.js
const { createAnimation } = require('lib/purgetss.ui')

const dragMotion = createAnimation({
animationProperties: { snap: { back: true } }
})
let dragReady = false

function enableDrag() {
if (dragReady) return
dragReady = true
dragMotion.draggable(piece)
}

function disposeWindow() {
dragMotion.undraggable(piece)
window.removeEventListener('open', enableDrag)
window.removeEventListener('close', disposeWindow)
dragReady = false
}

window.addEventListener('open', enableDrag)
window.addEventListener('close', disposeWindow)

Do not call draggable() repeatedly for the same view: the current module does not deduplicate registrations. undraggable() removes its touch and orientation listeners and most private drag state, but the current release leaves the private _wasDragged flag on the view. Release your animation, view, and callback references after closing a window so collision callbacks cannot keep an inactive UI alive.

Runtime utilities

Resources/app.js
const {
deviceInfo,
saveComponent
} = require('lib/purgetss.ui')

deviceInfo()

function savePreview() {
if (!Ti.Media.hasPhotoGalleryPermissions()) {
Ti.Media.requestPhotoGalleryPermissions((event) => {
if (event.success) savePreview()
})
return
}

saveComponent({
source: preview,
directory: Ti.Filesystem.applicationDataDirectory
})
}

deviceInfo() logs platform and display values and returns undefined. It includes xdpi and ydpi only on Android. Its tablet flag recognizes iPad only, so an Android tablet is currently logged as isTablet: false and isHandheld: true.

saveComponent() renders source.toImage(), creates an MD5-based PNG filename, writes it to directory (or Ti.Filesystem.tempDirectory), and then invokes Ti.Media.saveToPhotoGallery(). It also returns undefined and exposes no completion or error callback. Check/request gallery authorization with Ti.Media before calling it; on iOS, declare NSPhotoLibraryAddUsageDescription as required by Titanium.

Platform notes

  • transition() preserves the last transform when hiding a view on iOS. On Android it resets transform, translation, rotation, and scale before the next fade-in to avoid animator conflicts.
  • Android drag consolidates translation, rotate, scale, and the equivalent matrix on touch end. It does not rewrite the position as only top/left.
  • Animated zIndex is documented differently across Titanium platforms. Use a composite layout where stacking order matters and verify the result on both targets.
  • On Mac Catalyst, give parents of transitioned views fixed dimensions rather than Ti.UI.FILL; a resizable parent can distort rotated matrices.