44 |
Example Domain
45 |
This domain is established to be used for illustrative examples in documents. You may use this
46 | domain in examples without prior coordination or asking for permission.
47 |
More information...
48 |
49 |
50 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | flask
2 | requests
3 | bs4
4 |
--------------------------------------------------------------------------------
/static/img/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaytoun/cursor-tracking-css/b1f0ddbe7f44aded397b53fbf248a859bfaeb04c/static/img/demo.gif
--------------------------------------------------------------------------------
/static/js/playback.js:
--------------------------------------------------------------------------------
1 | document.addEventListener("DOMContentLoaded", function(event) {
2 |
3 | function sleep(ms) {
4 | return new Promise(resolve => setTimeout(resolve, ms));
5 | }
6 |
7 | async function playback(events) {
8 | for (var i = 0; i < events.length - 1; i++) {
9 | el = document.querySelector(events[i].selector);
10 | el.style.background = 'rgba(255, 0, 0, .5)';
11 | let difference = events[i+1].timestamp - events[i].timestamp;
12 | await sleep(difference);
13 | el.style.background = null;
14 | }
15 | }
16 |
17 | playback(events)
18 |
19 | })
--------------------------------------------------------------------------------
/tracker.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import string
4 | import random
5 | import hashlib
6 | import urllib.parse
7 | from bs4 import BeautifulSoup
8 |
9 | def generate_css(path, duration=100, keyframe_count=100):
10 | inject_template_tag(path)
11 |
12 | with open(path, 'r') as f:
13 | soup = BeautifulSoup(f.read(), 'html.parser')
14 |
15 | selectors = []
16 | generate_tag_selectors(soup, selectors, '')
17 |
18 | if not os.path.exists('./static/css/'):
19 | os.makedirs('./static/css/')
20 |
21 | with open('static/css/tracker.css', 'w') as f:
22 | for selector in selectors:
23 | animation_name = ''.join(random.choices(string.ascii_lowercase, k=6))
24 | f.write(generate_animation_keyframes(selector, animation_name, duration, keyframe_count))
25 | f.write(generate_animation_rule(selector, animation_name, duration))
26 | f.write(generate_hover_rule(selector))
27 |
28 | def generate_hover_rule(selector):
29 | return '{selector}:hover {{ -webkit-animation-play-state:running; -moz-animation-play-state:running; animation-play-state:running; }}\n'.format(selector=selector)
30 |
31 | def generate_animation_rule(selector, animation_name, duration):
32 | return '{selector} {{ -moz-animation: {animation_name} {duration}s infinite; -webkit-animation: {animation_name} {duration}s infinite; animation: {animation_name} {duration}s infinite; -webkit-animation-play-state:paused; -moz-animation-play-state:paused; animation-play-state:paused; }}\n'.format(selector=selector, animation_name=animation_name, duration=duration)
33 |
34 | def generate_animation_keyframes(selector, animation_name, duration, keyframe_count):
35 | keyframe_count = max(1, min(10000, keyframe_count))
36 | step_size = 100.00 / keyframe_count
37 | time_per_step = float(duration) / keyframe_count
38 |
39 | keyframes = []
40 | i = 0
41 | time = 0
42 | while i < 100:
43 | percentage, t = '{0:.2f}'.format(i), '{0:.2f}'.format(time)
44 | keyframes.append('%s%% { background-image: url("http://127.0.0.1:5000/tracker/%s/%s/"); }' % (percentage, urllib.parse.quote(selector), t))
45 | i += step_size
46 | time += time_per_step
47 |
48 | return '@keyframes %s { %s }\n' % (animation_name, ' '.join(keyframes))
49 |
50 | def inject_template_tag(path):
51 | with open(path, 'r') as f:
52 | html = f.read()
53 |
54 | if html.find('{css_tracker_import_line}') > -1:
55 | return
56 |
57 | html = html.replace('', '{css_tracker_import_line}')
58 |
59 | with open(path, 'w') as f:
60 | f.write(html)
61 |
62 | def generate_tag_selectors(s, selectors, selector):
63 | if getattr(s, 'name', None) == None:
64 | return
65 |
66 | if selector:
67 | selectors.append(selector)
68 |
69 | if hasattr(s, 'children'):
70 | i = 1
71 | for child in s.children:
72 | if getattr(child, 'name', None) == None:
73 | continue
74 | next_selector = selector + ' :nth-child(%s)' % (i)
75 | generate_tag_selectors(child, selectors, selector=next_selector)
76 | i += 1
77 |
78 | def main():
79 | if len(sys.argv) != 2:
80 | print('please include the path of the html file as an arg')
81 | return
82 | generate_css(sys.argv[1])
83 |
84 | if __name__ == '__main__':
85 | main()
86 |
--------------------------------------------------------------------------------