制作3D浪漫炫酷相册【附源码】

news/2024/10/23 9:36:04/

制作3D浪漫炫酷相册【附源码】

我的网站已经上线了 http://javapub.net.cn/

博主介绍: 🚀自媒体 JavaPub 独立维护人,全网粉丝15w+,csdn博客专家、java领域优质创作者,51ctoTOP10博主,知乎/掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和副业。🚀


公众号:JavaPub ⭐ ⭐简历模板、学习资料、面试题库等都给你💪


🍅 文末获取源码 🍅 无套路,免费领取

点赞再看,养成习惯

适合人群:初级学习者和爱好者,下面有展示图。计算机毕业设计

文章目录

    • 1 前言
    • 2 正文
      • 2.1 展示预览
      • 2.2 项目结构
      • 2.2 主要代码展示
      • 源码下载
    • 系列推荐:
    • [查看更多博主首页更多实战项目 >>>](https://blog.csdn.net/qq_40374604/category_11788364.html)
      • 源码获取:

1 前言

🚀获取源码,文末公众号回复【制作3D浪漫炫酷相册】,即可。
⭐欢迎点赞留言

2 正文

公众号:JavaPub

2.1 展示预览

14MB GIF可以欣赏:
https://tvax2.sinaimg.cn/large/007F3CC8ly1h3ko405nkzg30zp0lq7wu.gif

动画

2.2 项目结构

image

2.2 主要代码展示


<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title>制作3D浪漫炫酷相册</title><style type="text/css">* {margin: 0;padding: 0;list-style-type: none;}body {perspective: 5000px;}<header class="navbar navbar-inverse" role="banner"><div class="navbar-header"><button type="button" class="navbar-toggle pull-left" data-toggle="collapse" data-target="#navbar-collapse"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="index.html" title="Elementary HTML5 Canvas Game Engine based on Backbone."><img src="apple_touch_icon.png" /> Backbone Game Engine</a></div><div id="navbar-collapse" class="collapse navbar-collapse" role="navigation"><ul class="nav navbar-nav"><li><a href="index.html">Documentation</a></li><li><a href="examples.html">Examples</a></li></ul><ul class="nav navbar-nav navbar-right"><li class="github-icon"><a href="https://github.com/martindrapeau/backbone-game-engine" title="Fork me on Github"><img src="docs/github.png" />Github</a></li></ul></div></header><div class="container"><div class="row"><p>Gravity is implemented with a positive <code>yAcceleration</code>. Unless atop a tile, our character will fall. However it is constrained by the extent of the <code>Backbone.World</code> it is contained in. And will therefore stop falling when the bottom is reached.</p><h4>Collisions</h4><p>Our character detects collisions either from tiles or other characters to constrain its movements. It does so using collision detection method <code>findCollidings</code> from <code>Backbone.World</code>. Every <code>update</code>, collisions with other sprites are detected on the outline of the sprite:</p><p><img class="img-responsive" src="docs/hero-collisions.png" alt="Hero Collisions" /></p><p>Collisions are only handled when necessary. For instance, when jumping collisions are handled top and right only. The decision is based on looking at <code>velocity</code> and <code>yVelocity</code>. For gravity, a check is performed every time at the bottom of the sprite to land or to fall.</p><pre>
var bottomWorld = this.world.height() + </div><div id="documentation-Button" class="row"><div class="col-md-12"><h3>Backbone.Button</h3><p><code>Backbone.Button</code> is a <code>Backbone.Element</code> which listens to tap/click events and triggers a <code>tap</code> event when pressed. When pressed there is a grow-shrink animation to give the user feedback.</p><h4>Usage</h4><pre>
var button = new Backbone.Button({x: 4, y: 4, width: 52, height: 52, borderRadius: 5,img: "#icons", imgX: 0, imgY: 0, imgWidth: 32, imgHeight: 32, imgMargin: 10
});
button.on("tap", function() {console.log("button tapped!");
});
</pre></div></div><div id="documentation-DebugPanel" class="row"><div class="col-md-12"><h3>Backbone.DebugPanel</h3><p><code>Backbone.DebugPanel</code> is a Backbone model on which you set attributes to be dumped on screen. Upon draw, it will <code>JSON.stringify</code> attributes.</p><h4>Events</h4><ul><li><code>update(dt)</code>: No-op. Simply returns true.</li><li><code>draw(context)</code>: Draws the debug information on screen.</li><li><code>attach</code>: Triggered when the sprite is attached to the engine.</li><li><code>detach</code>: Triggered when the sprite is detached to the engine.</li></ul><h4>Usage</h4><pre>
var debugPanel = new Backbone.DebugPanel();
var engine = new Backbone.Engine({}, {debugPanel: debugPanel
});
engine.add(debugPanel);debugPanel.set({hello: "Word"});
// Draws this on screen
// {"fps": 58, "ct": 7, "hello": "World"}debugPanel.set({hello: "Dolly"});
// {"fps": 58, "ct": 7, "hello": "Dolly"}debugPanel.unset("hello");
// {"fps": 58, "ct": 7}
</pre><p>In the above example, the debug panel is created. It is added to the engine as a model to draw. It is also passed as an option to the engine so it can output <code>fps</code> and <code>ct</code> (cycle time).</p><p>We manually add attribute <code>hello</code> to be tracked. Whenever it changes, so does the print out on screen. Use <code>unset</code> to remove a tracked attribute.</p><h4>Conditional Usage</h4><p>It is recommended that you support the non-existence of the debug panel with an <code>if (this.debugPanel)</code> statement before setting. For example, when you extend a class, pass in the debug panel as an option. Then, in your code, check to see if it exists. For example, this is done in the <code>Backbone.Engine.draw</code> method:</p><pre>if (this.debugPanel) this.debugPanel.set({fps: this.fps, ct: this.cycleTime});</pre><p>This supports the case where the debug panel is never created (<code>debugPanel</code> = <code>undefined</code>), such as in production.</p></div></div><div id="documentation-Shapes" class="row"><div class="col-md-12"><h3>Shape functions</h3><p>File <code>shapes.js</code> contains helper functions to draw elementary shapes in the 2d drawing context of a canvas. You are free to use direct methods on the context to draw. These are provided as convenience. The functions are added to the global scope, under <code>window</code>. Supported functions are:</p><pre>
drawRect(ctx, x, y, width, height, fill, stroke)
drawCircle(ctx, x, y, radius, fill, stroke)
drawRoundRect(ctx, x, y, width, height, radius, fill, stroke)
</pre><p>I encourage you to add your own. If you do, respect these recommendations:</p><ul><li>Functions take as first argument <code>ctx</code> the drawing context.</li><li>Second and third arguments should be <code>x</code> and <code>y</code> coordinates.</li><li>Last arguments should be <code>fill</code> the fill style, and <code>stroke</code> the stroke style. They should be optional if possible.</li></ul></div></div><div id="mobile-devices" class="row"><div class="col-md-12"><h1>Mobile Devices</h1><p>Backbone Game Engine was built for mobile first.</p><h3>Touch Events</h3><p><a href="#documentation-Engine">Backbone.Engine</a>, <a href="#documentation-Input">Backbone.Input</a>, <a href="#documentation-Button">Backbone.Button</a> and <a href="#documentation-WorldEditor">Backbone.WorldEditor</a> support touch and mouse events transparently. Works on Android, iOS and Windows.</p><h3>Viewport resizing and canvas centering</h3><p>On mobile devices, the <code>meta</code> tag <code>viewport</code> is set to 960 pixels wide.On iOS, Android and Windows mobile devices, this will ensure the canvas is full width.The HTML file contains the necessary header tags to ensure everything works.You can change the viewport width value to whatever you want.</p>
<pre>
&lt;meta name="viewport" content="width=960, user-scalable=no"/&gt;
&lt;meta name="mobileoptimized" content="0" /&gt;
</pre><p>Not all screens have the same aspec ratio.To take care of the height, you can change the height of the canvas upon start by calling the global function <code>adjustViewport()</code> (see file <code>adjust-viewport.js</code> for details).</p>
<pre>
var canvas = document.getElementById("foreground");
adjustViewport(canvas);
</pre><p>If you want to maintain the aspect ratio, pass true. The canvas will be centered on screen.</p>
<pre>
var canvas = document.getElementById("foreground");
adjustViewport(canvas, true);
</pre><p>On desktop the <code>viewport</code> meta tag is ignored.<code>adjustViewport</code> will center the canvas, even handling resizes.It will try to reduce the height of the canvas if too tall unless you omit the <code>keepRatio</code> argument.</p><h3>Web App</h3><p>These meta tags are set to enable Web App support:</p>
<pre>
&lt;meta name="apple-mobile-web-app-capable" content="yes" /&gt;
&lt;meta name="mobile-web-app-capable" content="yes" /&gt;
&lt;meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/&gt;
</pre><p>To suggest users to put add the home page to the home screen, checkout this great plugin:<a href="https://github.com/cubiq/add-to-homescreen" target="_blank">Cubiq's Add To Homescreen</a>.</p></div></div><div id="going-offline" class="row"><div class="col-md-12"><h1>Going Offline</h1><p>With <a href="https://developer.apple.com/library/safari/documentation/iphone/conceptual/safarijsdatabaseguide/offlineapplicationcache/offlineapplicationcache.html" target="_blank">HTML Application Cache</a>, you can go offline with your game. <a href="super-mario-bros/index.html" target="_blank">Super Mario Bros level 1-1</a> uses the application cache. The first time your browser loads that page, it will save the web page, along with all assets in its application cache. Subsequent visits will load these from the application cache instead of the server.</p><div class="alert alert-info">Note: Application Cache only works when loaded from a server. It will not if you have forked the repo, and are loading the file from your disk (<code>file:///</code>). That's good because under development, we want to load the new code every refresh.</div><p>If you have Google Chrome, open the console and you will see this:</p><pre>
Creating Application Cache with manifest http://martindrapeau.github.io/backbone-game-engine/super-mario-bros/offline.appcache
Application Cache Checking event martindrapeau.github.io/:1
Application Cache Downloading event martindrapeau.github.io/:1
Application Cache Progress event (0 of 23) http://martindrapeau.github.io/backbone-game-engine/3rd/qtree.js
Application Cache Progress event (1 of 23) http://martindrapeau.github.io/backbone-game-engine/src/input.js
...
Application Cache Progress event (22 of 23) http://martindrapeau.github.io/backbone-game-engine/super-mario-bros/super-mario-enemies-2x.png
Application Cache Progress event (23 of 23)
Application Cache Cached event 
</pre><p>Subsequent times, you will see this:</p><pre>
Document was loaded from Application Cache with manifest http://martindrapeau.github.io/backbone-game-engine/super-mario-bros/offline.appcache
Application Cache Checking event
Application Cache NoUpdate event
</pre><h3>Manifest File</h3><p>Using an Application Cache is dead simple. First you must add the <code>manifest</code> attribute to your HTML tag. It points to the manifest file:</p><pre>
&lt;!doctype html&gt;
&lt;html manifest="offline.appcache"&gt;&lt;head&gt;
</pre>Second, create the <code>manifest</code> file. It contains files that must be cached. For example here is the <code>offline.appcache</code>:</p><pre>
CACHE MANIFEST
# Version 0.11 (c) 2014-2015 Martin Drapeau../3rd/qtree.js
../3rd/underscore.js
../3rd/backbone.native.js
../3rd/backbone.js../src/input.js
../src/shapes.js
../src/core.js
../src/world.js
../src/local-storage.js
../src/camera.js
../src/editor.js
../src/hero.jsmario.js
tiles.js
artifacts.js
enemies.js
display.js
level_1-1.js
main.jssuper-mario-2x.png
super-mario-tiles-2x.png
super-mario-enemies-2x.png
icons.png
</pre><p>If you have server requests, you can add a <code>NETWORK</code> section. Consult the docs for details.</p><p>Fianally, the comment with <code>Version 0.11</code> is important. When a new version of Super Mario Bros level 1-1 is released, the version number is increased to force the browser to reload the files. It will also trigger an <code>updateready</code> event which gets captured to show a download button. That informs the user a new version is ready to be downloaded. Clicking on that button simply refreshes the browser to reload the new version.</p></div></div><div id="persistence" class="row"><div class="col-md-12"><h1>Persistence</h1><p>Backbone offers RESTful persistence via <a href="http://backbonejs.org/#Sync" target="_blank">Backbone.sync</a>. Models have methods <code>fetch</code> and <code>save</code> to retrieve/send the model/collection JSONified data to/from the server. As such, you can easily implement server-side persistence using well established RESTful standards.</p><p>In our Super Mario Bros example, we use <a href="https://developer.apple.com/library/safari/documentation/iPhone/Conceptual/SafariJSDatabaseGuide/Name-ValueStorage/Name-ValueStorage.html" target="_blank">local storage</a> instead. This is done by overriding <code>Backbone.World</code> methods <code>save</code> and <code>fetch</code>. See file <code>src/local-storage.js</code> for details.</p></div></div><div id="performance" class="row"><div class="col-md-12"><h1>Performance and Debugging</h1><p>HTML5 canvas has come a long way in terms on performance. Browser implementations of canvas now offer impressive performance. On mobile, Apple leads the pack being able to sustain a 60fps for the Super Mario Bros example. However on Android, frame rates drop fast to the 30s when the background needs to be redrawn. On Surface performance seems good on newer models, however on first generation RT models, as slow as on Android tablets.</p><p>This being said, there are things you can do to ensure the best performance.</p><h4>Keep cycle time below 16ms</h4><p>That is the time you have between redraws, 60 times a second. The <code>Backbone.Engine</code> will report the frame rate (fps), and cycle time (ct) if you add and attach a <code>Backbone.DebugPanel</code>. Make sure to use it. If you see <code>fps</code> go down while <code>ct</code> goes up, then your <code>update</code> and <code>draw</code> times must be too long. You can time the <code>update</code> time to pinpoint the issue.</p><h4>Play well with Javascript Garbage Collection</h4><p>You can't avoid it. You will leak memory. Every call made by <code>requestAnimationFrame</code> creates a function scope. It does so 60 times a second and it will need to be garbage collected. The browser will pause to collect garbage.</p><p>You can however control the leakage rate. Try to create objects upfront, and pool resources as much as possible. That's why sprite sheets are shared among sprites. If you ever see that your game jerks, at an even interval (i.e. every 30s), then you are probably being hit by the garbage collector recuperating large amounts of memory (>10MB).</p><p>You can use the Timeline tool in Chrome/Safari Developer Tools to identify this. Record a session and once done, you can apply a filter <code>gc</code> to filter on garbage collection events. You will notice they are at evenly spaced intervals. On my machine, for Super Mario Bros, 3.5MB is collected every 4s on average. There is no jerk. No jerk on a tablet means healthy memory management.</p><p>Some further references and good resources on performance:</p><ul><li><a href="http://blog.artillery.com/2012/10/browser-garbage-collection-and-framerate.html" target="_blank">Browser Garbage Collection and Frame Rate</a></li><li><a href="https://www.scirra.com/blog/76/how-to-write-low-garbage-real-time-javascript" target="_blank">How to write low garbage real-time Javascript</a></li><li><a href="http://www.html5rocks.com/en/tutorials/canvas/performance/" target="_blank">Improving HTML5 Canvas Performance</a></li></ul></div></div><div id="publishing" class="row"><div class="col-md-12"><h1>Publishing your Game</h1><h2>On the Web</h2><p>If you forked this repo, your game is already published on the web on your Github page under <code>[username].github.io/backbone-game-engine</code>.</p><p>If you own an iPad or iPhone, you can add it to the home screen as a Web app. It will open in full-screen and if you've implemented an Application Cache, it will work offline too.</p><h2>On iOS and Android</h2><p>Backbone Game Engine was built to run in <a href="https://www.ludei.com/cocoonjs/" target="_blank">CocoonJS</a> canvas+. You can try out Super Mario Bros level 1-1 in the <a href="http://support.ludei.com/hc/en-us/articles/201048463-CocoonJS-launcher-user-guide" target="_blank">CocoonJS launcher</a> by pointing to the zip file at this URL: http://martindrapeau.github.io/cocoon-mario/cocoon-mario.zip.</p>
<pre>
http://martindrapeau.github.io/cocoon-mario/cocoon-mario.zip
</pre><p>Checkout the Github repo <a href="" target="_blank">cocoon-mario</a>.It can be used as the basis for your own native game on iOS or Android.</p></div></div><div id="change-log" class="row"><div class="col-md-12"><h1>Change Log</h1><h4>0.40 - TBD</h4><ul><li>Upcoming release to include bug fixes, improvements and new features to come following the release to iOS of Ludo's Quest.</li></ul><h4>0.30 - 2015-03-22</h4><ul><li>Backbone.Element - a rudimentary DOM element with image, text and animations.</li><li>Backbone.World now uses a QuadTree for collision detection.</li><li>Removed dependence on hammer.js. Backbone.Engine now triggers tap and key events.</li><li>Complete rewrite of Backbone.Input. Removed its pause button.</li><li>Complete rewrite of Backbone.Character.</li><li>Complete rewrite of Backbone.Hero.</li><li>Backbone.Editor now resizes sprites to fit in the specified tileWidth and tileHeight.</li><li>Rewrite of adjustViewport global function to work cross-device.</li><li>Official support of CocoonJS canvas+.</li></ul><h4>0.21 - 2015-02-06</h4><ul><li>Sprite padding</li><li>More efficient gamepad drawing</li><li>Editor: paging, shrink large sprites, highlight tiles</li><li>World: z-index, tap event,key event, fixed background image, improved sprite lookup, bug fixes</li></ul><h4>0.20 - 2014-12-31</h4><p>Major improvements including:<ul><li>Performance improvements.</li><li>Fast sprite lookup.</li><li>Faster dynamic and static drawing.</li><li>Efficient collision detection.</li><li>Character and hero knockout and dying.</li><li>Bug fixes.</li></ul></p><h4>0.11 - 2014-11-12</h4><p>Adjust viewport on orientation change, and center canvas.</p><h4>0.10 - 2014-05-19</h4><p>Initial release.</p></div></div></div><div class="col-md-3"><div id="sidebar" class="bs-sidebar affix"><ul class="nav bs-sidenav"><li class="active"><a href="#introduction">Introduction</a></li><li><a href="#getting-started">Getting Started</a></li><li><a href="#documentation">Reference</a></li><li><a href="#documentation-Engine">&nbsp;&nbsp;Backbone.Engine</a></li><li><a href="#documentation-SpriteSheet">&nbsp;&nbsp;Backbone.SpriteSheet</a></li><li><a href="#documentation-SpriteSheetCollection">&nbsp;&nbsp;Backbone.SpriteSheetCollection</a></li><li><a href="#documentation-Sprite">&nbsp;&nbsp;Backbone.Sprite</a></li><li><a href="#documentation-Input">&nbsp;&nbsp;Backbone.Input</a></li><li><a href="#documentation-World">&nbsp;&nbsp;Backbone.World</a></li><li><a href="#documentation-WorldEditor">&nbsp;&nbsp;Backbone.WorldEditor</a></li><li><a href="#documentation-Character">&nbsp;&nbsp;Backbone.Character</a></li><li><a href="#documentation-Hero">&nbsp;&nbsp;Backbone.Hero</a></li><li><a href="#documentation-Camera">&nbsp;&nbsp;Backbone.Camera</a></li><li><a href="#documentation-Clock">&nbsp;&nbsp;Backbone.Clock</a></li><li><a href="#documentation-Element">&nbsp;&nbsp;Backbone.Element</a></li><li><a href="#documentation-Button">&nbsp;&nbsp;Backbone.Button</a></li><li><a href="#documentation-DebugPanel">&nbsp;&nbsp;Backbone.DebugPanel</a></li><li><a href="#documentation-Shapes">&nbsp;&nbsp;Shape functions</a></li><li><a href="#mobile-devices">Mobile Devices</a></li><li><a href="#going-offline">Going Offline</a></li><li><a href="#persistence">Persistence</a></li><li><a href="#performance">Performance</a></li><li><a href="#publishing">Publishing</a></li><li><a href="#change-log">Change Log</a></li></ul></div></div></div></div><br/><footer class="navbar navbar-default"><p class="navbar-text navbar-left">&copy; 2014 <a href="http://martindrapeau.tumblr.com/">Martin Drapeau.</a><a href="https://github.com/martindrapeau/backbone-game-engine/blob/gh-pages/LICENSE">Licensed under MIT.</a></p><p class="navbar-text navbar-right">Written in Montréal, Canada.</p><p class="navbar-text navbar-right">&nbsp;</p></footer></body>
</html>

请添加图片描述

源码下载

获取源码,公众号回复【制作3D浪漫炫酷相册】,即可。更多最新Java面试题加群、见群公告。~

不会还有人没 点赞 + 关注 + 收藏 吧!

在这里插入图片描述

系列推荐:

Java 实现 捕鱼达人 小游戏【附源码】

Java 实现 贪吃蛇 小游戏【附源码】

Java 实现 1024 小游戏【附源码】

Java实现一个坦克大战的小游戏【附源码】

Java实现一个打飞机的小游戏【附源码】

Java 实现 植物大战僵尸 小游戏【附源码】

制作温馨浪漫爱心表白动画特效HTML5+jQuery【附源码】

基于SpringBoot+Bootstrap【爱码个人博客系统】附源码

基于SSM【爱校图书馆管理系统】附源码+论文

查看更多博主首页更多实战项目 >>>

项目源码获取方法

可以开始点赞本文、点赞本文,然后私信我,我免费分享给你哦~

源码获取:

大家点赞、收藏、关注、评论啦 、查看👇🏻👇🏻👇🏻微信公众号获取联系方式👇🏻👇🏻👇🏻


精彩专栏推荐订阅:在下方专栏👇🏻👇🏻👇🏻👇🏻


Java项目精品实战案例《101套》


web前端期末大作业网页实战《365套》


http://www.ppmy.cn/news/277630.html

相关文章

56相册视频(土豆相册视频 激动相册视频 QQ动感影集等)——下载教程

由于目前流行的相册视频或影集大多是由Flash、音乐和图片组合而成的动画&#xff0c;不属于完整视频&#xff0c;所以不能用常规的解析方法下载。 鉴于很多朋友希望可以下载自己精心制作的相册&#xff0c;故在本教程中&#xff0c;我们将以图文并茂的方式为大家介绍详细的相册…

【3D相册】零基础完成3D相册并配上背景音乐

文章目录 一、前言二、准备工作1、新建文件夹2、准备素材对于图片的处理对于音乐的处理 三、代码工作1、python处理6张图片1.1代码运行1.2 放入背景图片 2、写html文件2.1 更换音乐素材 3、运行main.html 四、推荐阅读 一、前言 帮助好哥们整的一个小相册&#xff0c;给他写个…

搭载3D立体相册网页 加入背景音乐 真香!

一、3D立体相册 HTML代码 <!DOCTYPE html> <html lang="en"><head><meta

动感相册源码--类似QQ动感影集.rar

http://down.51vip.net/soft/download.asp?softid44540&downid54&id44502&urlyz44639 http://soft.51vip.com/down_51vip_net/20080926/down_51vip_com_动感相册源码--类似QQ动感影集.rar

教你用 CSS 实现超真实的 3D 相册,让你的照片立体感 UPUP

前言 现如今网页越来越趋近于动画&#xff0c;相信大家平时浏览网页或多或少都能看到一些动画效果&#xff0c;今天我们来做一个有意思的动画效果&#xff0c;通过 css3 实现 3d 效果的立方体相册&#xff0c;下面一起看看吧。 实现思路 首先我们要确定好 html 的结构以及要使…

Android App开发实战项目之仿手机QQ动感影集动画播放(附源码和演示视频 可直接使用)

需要图片集和源码请点赞关注收藏后评论区留言~~~ 动感影集就是只要用户添加一张图片&#xff0c;动感影集就能给每张图片渲染不同的动画效果&#xff0c;让原本静止的图片变得活泼起来&#xff0c;辅以各种精致的动画特效&#xff0c;营造一种赏心悦目的感觉。 一、需求描述 …

html制作相册影集,影集相册制作系统

影集相册制作系统正式版 影集相册制作系统正式版是款专业性与实用性都很强的相册制作工具。影集相册制作系统可以把我们日照拍摄的照片或者新婚照片制作成一个视频&#xff0c;还拥有超强的照片编辑功能和图片编辑功能。影集相册制作系统通过200多种的播放方式展现您的照片,可以…

c语言做相册影集,制作DIY相册影集教程-如何制作影集

DIY电子相册近年来十分流行&#xff0c;亲手制作电子相册&#xff0c;存储美好回忆。DIY亲手制作最能代表心意&#xff0c;不仅增添生活中的仪式感&#xff0c;还能增加情感交流。如今越来越多的场景使用到电子相册&#xff0c;可是对于很多小白来说&#xff0c;制作一个精美的…