(defun animate-initialize (string vpos hpos)
(let ((characters nil))
(dotimes (i (length string))
(setq characters
(cons (list (aref string i)
(random (window-height))
(random (1- (window-width)))
vpos
(+ hpos i))
characters)))
characters))
(defun animate-step (characters fraction)
(let ((remains (- 1 fraction)))
(dolist (item characters)
(let ((vpos (+ (* remains (nth 1 item))
(* fraction (nth 3 item))))
(hpos (+ (* remains (nth 2 item))
(* fraction (nth 4 item)))))
(animate-place-char (car item) vpos hpos)))))
(defun animate-place-char (char vpos hpos)
(goto-char (window-start))
(let (abbrev-mode)
(dotimes (i vpos)
(end-of-line)
(if (= (forward-line 1) 1)
(insert "\n"))))
(beginning-of-line)
(move-to-column (floor hpos) t)
(unless (eolp) (delete-char 1))
(insert-char char 1))
(defvar animate-n-steps 10
"Number of steps to use `animate-string'.")
(defun animate-string (string vpos &optional hpos)
"Display STRING starting at position VPOS, HPOS, using animation.
The characters start at randomly chosen places,
and all slide in parallel to their final positions,
passing through `animate-n-steps' positions before the final ones.
If HPOS is nil (or omitted), center the string horizontally
in the current window."
(let ((characters
(animate-initialize string vpos
(or hpos
(max 0 (/ (- (window-width) (length string)) 2))))))
(dotimes (i animate-n-steps)
(let (buffer-undo-list
list-to-undo)
(animate-step characters (/ i 1.0 animate-n-steps))
(set-window-start nil 1)
(sit-for .05)
(setq list-to-undo buffer-undo-list)
(while list-to-undo
(let ((undo-in-progress t))
(setq list-to-undo (primitive-undo 1 list-to-undo))))))
(animate-step characters 1)
(end-of-line)
(sit-for 0)
(undo-boundary)))
(defun animate-sequence (list-of-strings space)
"Display strings from LIST-OF-STRING with animation in a new buffer.
Strings will be separated from each other by SPACE lines."
(let ((vpos (/ (- (window-height)
1 (* (1- (length list-of-strings)) space)
(length list-of-strings))
2)))
(switch-to-buffer (get-buffer-create "*Animation*"))
(erase-buffer)
(sit-for 0)
(setq indent-tabs-mode nil)
(while list-of-strings
(animate-string (car list-of-strings) vpos)
(setq vpos (+ vpos space 1))
(setq list-of-strings (cdr list-of-strings)))))
(defun animate-birthday-present (&optional name)
"Display one's birthday present in a new buffer.
You can specify the one's name by NAME; the default value is \"Sarah\"."
(interactive (list (read-string "Name (default Sarah): "
nil nil "Sarah")))
(switch-to-buffer (get-buffer-create (format "*%s*" name)))
(erase-buffer)
(sit-for 0)
(setq indent-tabs-mode nil)
(animate-string "Happy Birthday," 6)
(animate-string (format "%s" name) 7)
(sit-for 1)
(animate-string "You are my sunshine," 10 30)
(sit-for .5)
(animate-string "My only sunshine." 11 30)
(sit-for .5)
(animate-string "I'm awful sad that" 12 30)
(sit-for .5)
(animate-string "You've moved away." 13 30)
(sit-for .5)
(animate-string "Let's talk together" 15 30)
(sit-for .5)
(animate-string "And love more deeply." 16 30)
(sit-for .5)
(animate-string "Please bring back" 17 30)
(animate-string "my sunshine" 18 34)
(animate-string "to stay!" 19 34))
(random t)
(provide 'animate)