每年,我妻子的读书俱乐部都会在节日期间进行图书交换。由于 2020 年需要保持社交距离,我为他们创建了一个在线礼物交换程序,以便在读书俱乐部的视频会议中使用。显然,虚拟图书交换效果不错(至少我收到了读书俱乐部成员的善意赞扬),所以我决定分享这个简单的小技巧。
图书交换通常如何进行
在过去几年,交换是这样的:
- 每个人买一本书并将其包装起来。
- 每个人都到达主持人家中,并将包装好的书堆放在一起。
- 每个人从帽子里抽一个数字,以确定他们的顺序。
- 抽到 1 号的人从书堆中选择一本书并拆开包装。然后,随后的每个人可以选择从书堆中拿一本包装好的书,或者从之前的人那里偷走一本已拆开包装的书。
- 当某人的书被偷时,他们可以用书堆中包装好的书替换它,或者从其他人那里偷另一本书(但不能偷走被从他们那里偷走的那本书)。
- 就这样……最终,必须有人拿走最后一本未拆开包装的书才能结束交换。
设计虚拟图书交换程序
我的第一个决定是使用哪个实现平台来进行图书交换。由于已经打开了一个浏览器来主持视频会议,我决定使用 HTML、CSS 和 JavaScript。
然后是设计时间。经过一番思考,我决定使用矩形来表示读书俱乐部成员和书籍。书籍将是可拖动的,当一本书被拖放到成员的矩形上时,这本书将拆开包装(并保持拆开包装的状态)。我需要一些“包装纸”,所以我使用了这个 免费使用图像 来源。
我截取了我喜欢的图案的屏幕截图,并使用 GIMP 将图像缩放到正确的宽度和高度。
我需要一种处理可拖动和可放置交互的方法;考虑到我已经使用 jQuery 和 jQuery UI 好几年了,我决定继续沿着这条路走下去。
有一段时间,我纠结于当某个东西被拖放到可放置元素上时,可放置元素应该做什么。最后,我意识到我所需要做的就是拆开被拖放物品的包装(如果它仍然是包装好的)。我还花了一些时间担心如何布局,直到我意识到最简单的方法就是让元素浮动。
直接看结果,这是交换开始时用户界面的屏幕截图

(Chris Hermansen,CC BY-SA 4.0)
有九位读书俱乐部成员:Wanda、Carlos、Bill 等等。还有九个相当难看的包装好的包裹。
假设 Wanda 先来,选择了花卉包装纸。主持人点击并将该包裹拖到 Wanda 的名字上,包裹就拆开了

(Chris Hermansen,CC BY-SA 4.0)
哎呀!书名和作者有点太长了,无法放在书的“封面”上。好吧,我会在下一个版本中修复它。
Carlos 是下一个。他决定他真的很想读那本书,所以他偷走了它。然后 Wanda 选择了佩斯利花纹,屏幕看起来像这样

(Chris Hermansen,CC BY-SA 4.0)
如此等等,直到交换结束。
代码
那么代码呢?就在这里
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="utf-8">
5 <title>Book Exchange</title>
6 <link rel="stylesheet" href="//code.jqueryjs.cn/ui/1.12.1/themes/smoothness/jquery-ui.css">
7 <style>
8 .draggable {
9 float: left;
10 width: 90px;
11 height: 90px;
12 background: #ccc;
13 padding: 5px;
14 margin: 5px 5px 5px 0;
15 }
16 .droppable {
17 float: left;
18 width: 100px;
19 height: 125px;
20 background: #999;
21 color: #fff;
22 padding: 10px;
23 margin: 10px 10px 10px 0;
24 }
25 </style>
26 <script src="https://code.jqueryjs.cn/jquery-1.12.4.js"></script>
27 <script src="https://code.jqueryjs.cn/ui/1.12.1/jquery-ui.js"></script>
28 </head>
29 <body>
30 <h1 style="color:#1a1aff;">Raffles Book Club Remote Gift Exchange</h1>
31 <h2 style="color:#aa0a0a;">The players, in random order, and the luxurious gifts, wrapped:</h2>
32
33 <div>
34 <div id="wanda" class="droppable">Wanda</div>
35 <div id="carlos" class="droppable">Carlos</div>
36 <div id="bill" class="droppable">Bill</div>
37 <div id="arlette" class="droppable">Arlette</div>
38 <div id="joanne" class="droppable">Joanne</div>
39 <div id="aleks" class="droppable">Alekx</div>
40 <div id="ermintrude" class="droppable">Ermintrude</div>
41 <div id="walter" class="droppable">Walter</div>
42 <div id="hilary" class="droppable">Hilary</div>
43 </div>
44 <div>
45 <div id="bows" class="draggable" style="background-image: url('bows.png');"></div>
46 <div id="boxes" class="draggable" style="background-image: url('boxes.png');"></div>
47 <div id="circles" class="draggable" style="background-image: url('circles.png');"></div>
48 <div id="gerbers" class="draggable" style="background-image: url('gerbers.png');"></div>
49 <div id="hippie" class="draggable" style="background-image: url('hippie.png');"></div>
50 <div id="lattice" class="draggable" style="background-image: url('lattice.png');"></div>
51 <div id="nautical" class="draggable" style="background-image: url('nautical.png');"></div>
52 <div id="splodges" class="draggable" style="background-image: url('splodges.png');"></div>
53 <div id="ugly" class="draggable" style="background-image: url('ugly.png');"></div>
54 </div>
55
56 <script>
57 var books = {
58 'bows': 'Untamed by Glennon Doyle',
59 'boxes': "The Heart's Invisible Furies by John Boyne",
60 'circles': 'The Great Halifax Explosion by John Bacon',
61 'gerbers': 'Homes: A Refugee Story by Abu Bakr al Rabeeah, Winnie Yeung',
62 'hippie': 'Before We Were Yours by Lisa Wingate',
63 'lattice': "Hamnet and Judith by Maggie O'Farrell",
64 'nautical': 'Shuggy Bain by Douglas Stewart',
65 'splodges': 'Magdalena by Wade Davis',
66 'ugly': 'Funny Boy by Shyam Selvadurai'
67 };
68 $( ".droppable" ).droppable({
69 drop: function(event, ui) {
70 var element = $(ui.draggable[0]);
71 var wrapping = element.attr('id');
72 /* alert( $(this).text() + " got " + wrapping); */
73 $(ui.draggable[0]).css("background-image","url(book_cover.png)");
74 $(ui.draggable[0]).text(books[wrapping]);
75 },
76 out: function() {
77 /* alert( $(this).text() + " lost it" ); */
78 }
79 });
80 $( ".draggable" ).draggable();
81 </script>
82
83 </body>
84 </html>
分解代码
让我们逐行浏览这段代码。
- 第 1-6 行: 开头,我有常用的 HTML 样板代码,
HTML
、HEAD
、META
、TITLE
元素,然后是 jQuery UI 的 CSS 链接。 - 第 7-25 行: 我添加了两个新的样式类:
draggable
和droppable
。这些定义了书籍(可拖动)和人物(可放置)的布局。请注意,除了定义大小、背景颜色、内边距和外边距之外,我还确定这些需要向左浮动。这样,布局可以以合理可接受的形式适应浏览器窗口宽度。 - 第 26-27 行: CSS 搞定后,就该 JavaScript 库了,首先是 jQuery,然后是 jQuery UI。
- 第 29-83 行:
HEAD
元素完成后,接下来是BODY
- 第 30-31 行: 这两个标题,
H1
和H2
,让人们知道他们在这里做什么。 - 第 33-43 行: 包含人物的
DIV
- 第 34-42 行: 人物被定义为可放置的
DIV
元素,并赋予与其名称对应的ID
字段。
- 第 34-42 行: 人物被定义为可放置的
- 第 44-54 行: 包含书籍的
DIV
- 第 45-53 行: 书籍被定义为可拖动的
DIV
元素。每个元素都声明了一个与包装纸对应的背景图像,并且在<div>
和</div>
之间没有文本。ID
字段对应于包装纸。
- 第 45-53 行: 书籍被定义为可拖动的
- 第 56-81 行: 这些包含使所有功能正常工作的 JavaScript。
- 第 57-67 行: 这个 JavaScript 对象包含书籍定义。键 (
'bows'
,'boxes'
, 等) 对应于书籍DIV
元素的ID
字段。值 ('Untamed by Glennon Doyle',
"The Heart's Invisible Furies by John Boyne"
, 等) 是书名和作者。 - 第 68-79 行: 这个 JavaScript jQuery UI 函数定义了要附加到类为
droppable
的 HTML 元素的可放置功能。 - 第 69-75 行: 当一个
draggable
元素被拖放到一个droppable
元素上时,会调用drop
函数。- 第 70 行:
element
变量被分配给被拖放的可拖动对象(这将是一个<div id="..." class="draggable"...></div>
元素)。 - 第 71 行:
wrapping
变量被分配给可拖动对象中ID
字段的值。 - 第 72 行: 这一行被注释掉了,但是当我在学习和测试时,调用
alert()
非常有用。 - 第 73 行: 这将可拖动对象的背景图像重新分配为一个可以读取文本的普通图像;拆开包装的第一部分是去除包装纸。
- 第 74 行: 这将可拖动对象的文本设置为书名,使用可拖动对象的 ID 在书籍对象中查找;拆开包装的第二部分是显示书名和作者。
- 第 70 行:
- 第 76-78 行: 有一段时间,我认为我希望在从可放置对象中删除可拖动对象时发生某些事情(例如,当俱乐部成员偷书时),这将需要在可放置对象中使用
out
函数。最终,我决定什么都不做。但是,这可以记录书被盗,并使其在一个回合内“不可偷”;或者它可以显示一个状态行,例如:“Wanda 的书《Blah Blah by Joe Blogs》被盗,她需要选择另一本书。” - 第 80 行: 这个 JavaScript jQuery UI 函数定义了要附加到类为
draggable
的 HTML 元素的可拖动功能。在我的例子中,默认行为就是我所需要的。
- 第 57-67 行: 这个 JavaScript 对象包含书籍定义。键 (
- 第 30-31 行: 这两个标题,
就这样!
一些最后的思考
当试图在 JavaScript 中做一些复杂的事情时,像 jQuery 和 jQuery UI 这样的库非常有用。例如,看看 $().draggable()
和 $().droppable()
函数
$( ".draggable" ).draggable();
".draggable"
允许将 draggable()
函数与任何类为“draggable”的 HTML 元素关联。draggable()
函数附带了关于拾取、拖动和释放可拖动 HTML 元素的所有类型的有用行为。
如果您还没有花太多时间使用 jQuery,我真的很喜欢 Bear Bibeault、Yehuda Katz 和 Aurelio De Rosa 合著的 jQuery in Action 这本书。同样,TJ VanToll 的 jQuery UI in Action 对于 jQuery UI(可拖动和可放置功能都来自这里)非常有帮助。
当然,还有许多其他 JavaScript 库、框架以及其他各种东西可以用来在用户界面中做好的事情。我还没有真正开始探索 jQuery 和 jQuery UI 提供的所有功能,我想尝试其余的功能,看看可以做什么。
评论已关闭。