棱镜计划

View demo 查看演示Download Source 下载源

Today we’d like to show you how to build a simple slider with an interesting “prism” effect. The idea is to place a shape in front of the slider and “reflect” the images of each slide, in order to create the illusion of a prism. We’ll be using the HTML5 canvas element and plain JavaScript.

今天,我们想向您展示如何构建一个具有有趣的“棱镜”效果的简单滑块。 这个想法是在滑块的前面放置一个形状,并“反射”每个幻灯片的图像,以产生棱镜的错觉。 我们将使用HTML5 canvas元素和纯JavaScript

The demo is supported in all major browsers, including Internet Explorer 9.

所有主要浏览器(包括Internet Explorer 9)都支持该演示。

技术 (The Technique)

The technique used to create the effect is actually pretty simple: first we’ll load and render the mask, which could be either an SVG or a PNG image (the important thing is that it has to have transparency), then we will render the slide’s image and apply the globalCompositeOperation.

用于创建效果的技术实际上非常简单:首先,我们将加载并渲染遮罩,该遮罩可以是SVG图像或PNG图像(重要的是它必须具有透明度),然后我们将渲染该遮罩。幻灯片的图像并应用globalCompositeOperation 。

The ‘globalCompositeOperation’ canvas property lets you define how an image should be drawn over another image. By default, when we draw an image over existing pixels, the new image just replaces those pixels.

“ globalCompositeOperation”画布属性可让您定义如何在另一个图像上绘制图像。 默认情况下,当我们在现有像素上绘制图像时,新图像将替换这些像素。

By using globalCompositeOperation we will define how the image (source) is drawn onto the mask (destination). There are twelve composite operations and the one that suits our case is source-atop which will display the image on top of the mask and will not show anything outside the area defined by the mask.

通过使用globalCompositeOperation,我们将定义如何将图像(源)绘制到蒙版(目标)上。 有十二种复合操作,适合我们情况的一种操作是在源头上进行操作,它将在蒙版顶部显示图像,而不会在蒙版定义的区域之外显示任何内容。

The key to the effect is to draw the mask before the image, otherwise, since all pixels are empty initially, the ‘source-atop’ operation will not take any effect.

效果的关键是在图像之前绘制遮罩,否则,由于所有像素最初都是空的,因此“ source-atop”操作将不起作用。

In order to create the full effect we’ll need a layer for each part of our prism that we want to reflect the image in different ways. To do this we’ll use a layering technique, which means that we’ll have a separate canvas element for each layer. Then we’ll simply position them absolutely and place the canvases on top of each other.

为了创建完整的效果,我们需要为棱镜的每个部分都需要一个层,以便以不同的方式反射图像。 为此,我们将使用分层技术,这意味着我们将为每个图层使用单独的canvas元素。 然后,我们将它们完全定位,并将画布彼此叠放。

The reason why we would need to layer multiple canvases is because by W3C definition there is only one CanvasRenderingContext2D object per canvas, so in order to apply different effects on distinct parts (masks) of the prism at the same time we’ll need multiple contexts.

我们需要对多个画布进行分层的原因是,根据W3C的定义,每个画布只有一个CanvasRenderingContext2D对象,因此,为了同时对棱镜的不同部分(蒙版)应用不同的效果,我们需要多个上下文。

We’ll have a look at this in more detail later. Let’s start with the HTML structure and some styles.

我们稍后将对此进行更详细的介绍。 让我们从HTML结构和一些样式开始。

HTML和CSS (The HTML and CSS)

The only bits of HTML that we need for the slideshow initially, are a division where we append the canvases and an unordered list for the navigation bullets:

最初,幻灯片放映所需HTML唯一内容是一个分区,在其中添加画布和导航项目符号的无序列表:

<div class="container">
<ul class="navigation"></ul>
</div>

And here it is the required CSS:

这是必需CSS:

.prism-slider {
width: 1200px;
max-width: 100%;
height: 0;
padding-bottom: 48%;
position: relative;
}.prism-slider canvas {
width: 100%;
position: absolute;
top: 0;
left: 0;
}.navigation {
width: 100%;
position: absolute;
bottom: 5%;
text-align: center;
list-style: none;
z-index: 1;
}.navigation li {
border: 3px solid #eceff1;
width: 18px;
height: 18px;
margin: 0 5px;
background: #52525a;
border-radius: 50%;
display: inline-block;
cursor: pointer;
}.navigation .active {
background: #eceff1;
}

We also need to pre-load the external resources for images and masks before the initialization of the slider, otherwise we’ll get an empty slider until the images are loaded. To do this we’ll use a division with the class “cache” which will have have an img element for each image/mask:

我们还需要在初始化滑块之前预先加载图像和遮罩的外部资源,否则我们将获得一个空的滑块,直到图像被加载为止。 为此,我们将对“ cache”类使用一个除法,该除法将为每个图像/蒙版包含一个img元素:

<div class="cache">
<!-- masks -->
<img src="img/masks/cube-a.svg">
<img src="img/masks/cube-b.svg">
<img src="img/masks/cube-c.svg">
<!-- photos -->
<img src="img/shoreditch-a.jpg">
<img src="img/shoreditch-b.jpg">
<img src="img/shoreditch-c.jpg">
</div>

Then we simply hide it with display: none and initialize the plugin on window.onload.

然后我们只用display: none隐藏它display: none并在window.onload上初始化插件。

JavaScript (The JavaScript)

The JavaScript is split in two modules: slideshow.js which acts as a controller, and PrismSlider.js which is the class responsible for the creation and rendering of each canvas layer.

JavaScript分为两个模块:充当控制器的slideshow.js和负责创建和渲染每个画布层的类PrismSlider.js

Let’s first have a look at the main JavaScript of the Prism Slider and its first method that we call:

首先让我们看一下Prism Slider的主要JavaScript及其调用的第一个方法:

/**
* Create canvas element, get context, set sizes
* and append to main container.
*/
PrismSlider.prototype.addCanvas_ = function() {this.canvas = document.createElement('canvas');this.context = this.canvas.getContext('2d');this.canvas.width = this.settings.container.sizes.w;
this.canvas.height = this.settings.container.sizes.h;this.container.appendChild(this.canvas);
};

Now that we have a canvas element as mentioned earlier we need to add and draw the mask:

现在我们有了前面提到的canvas元素,我们需要添加和绘制蒙版:

/**
* Add Mask.
* Call loadImage method with path and callback,
* once the loading will be completed we'll replace
* the string path (this.mask.source) reference with
* the actual <img> object.
*/
PrismSlider.prototype.addMask_ = function() {var path = this.mask.source;
var callback = this.renderMask_.bind(this);
// Replace image path with <img> object.
this.mask.source = this.loadImage_(path, callback);
};/**
* Draw mask.
* Calculate center position and draw mask, width and height at 100% of the container sizes.
*/
PrismSlider.prototype.renderMask_ = function() {
var centerX = this.canvas.width / 2 - this.settings.container.sizes.w / 2;
var centerY = this.canvas.height / 2 - this.settings.container.sizes.h / 2;var w = this.settings.container.sizes.w;
var h = this.settings.container.sizes.h;this.context.drawImage(this.mask.source, centerX, centerY, w, h);
};

In the snippets above we used the loadImage method, since the browser has already cached all images at this point (because the script started after the document loaded) we can get the SVG mask and proceed without delaying the execution.

在上面的代码段中,我们使用了loadImage方法,因为此时浏览器已经缓存了所有图像(因为脚本在加载文档后启动),所以我们可以获得SVG掩码并继续执行而不会延迟执行。

/**
* Load image source from path and fire given callback,
* return loaded <img> object.
* @param  {String}   path     The path of the file.
* @param  {Function} callback The callback to be executed when loading completed.
* @return {Object}            The JavaScript <img> object.
*/
PrismSlider.prototype.loadImage_ = function(path, callback) {var image = new Image();image.onload = callback;// Path always after callback.
image.src = path;return image;
};

Now that we added and drew the mask let’s add some slides in a similar way:

现在,我们添加并绘制了蒙版,让我们以类似的方式添加一些幻灯片:

/**
* Add Slides.
* Call loadImage method for each image path in the slides array,
* only when it's the first slide pass render callback,
* when loading completed replace image path with
object. this.slides[i] = this.loadImage_(path, callback);}, this); };

The rendering callback is a little bit more complex:

渲染回调稍微复杂一点:

  • we get a couple of arguments, the index from the loop addSlides_ and a progress value that we don’t need for now but we make sure that it’s not going to be anything different from a number (like an event derived from image.onload).

    我们得到了几个参数,来自循环addSlides_的索引,以及我们现在不需要的进度值,但我们确保它与数字没有什么不同(例如从image.onload派生的事件) 。

  • Notice how we calculate the X position and remember that i is a number between 0 and the length of the slides that we will use.注意我们如何计算X位置,并记住i是介于0和将要使用的幻灯片长度之间的数字。
  • We also apply the composite operation only if we have a mask to render.仅当我们要渲染遮罩时,我们才应用复合操作。
  • Finally, we apply some effects right before the drawing.最后,我们在图纸之前应用一些效果。

The code:

代码:

/**
* Draw Slide.
* Calculate frame position, apply composite operation
* and effects on the image when there is a mask.
* @param  {Number} i        The index used to get the img to render.
* @param  {Number} progress The progress value.
*/
PrismSlider.prototype.renderSlide_ = function(i, progress) {// Set progress to 0 if Not a Number or undefined.
progress = (isNaN(progress) || progress === undefined) ? 0 : progress;// Get img object from array.
var slide = this.slides[i];// Calculate X position.
var x = this.canvas.width * (i - progress);
var y = 0;var w = this.canvas.width;
var h = this.canvas.height;// Apply composite operation.
if (this.mask) this.context.globalCompositeOperation = 'source-atop';this.context.save();if (this.mask) this.applyEffects_();// Draw slide.
this.context.drawImage(slide, x, y, w, h);this.context.restore();
};

The applyEffects method will just select one or both effects which will change the context before the drawing and will distinguish the image inside the mask from the main image on the slider.

applyEffects方法将仅选择一个或两个效果,这将在绘制之前更改上下文,并将遮罩内的图像与滑块上的主图像区分开。

/**
* Apply effects.
* Check mask object parameters and select effect.
*/
PrismSlider.prototype.applyEffects_ = function() {
if (this.mask.effects.flip) this.flip_();
if (this.mask.effects.rotate > 0) this.rotate_();
};/**
* Flip Effect.
*/
PrismSlider.prototype.flip_ = function() {
// Get axes.
var axes = this.mask.effects.flip;if (axes === 'X') {
// Invert x position.
this.context.translate(this.canvas.width, 0);
// Flip context horizontally.
this.context.scale(-1, 1);
}if (axes === 'Y') {
// Invert y position.
this.context.translate(0, this.canvas.height);
// Flip context vertically.
this.context.scale(1, -1);
}
};/**
* Rotate Effect.
*/
PrismSlider.prototype.rotate_ = function() {
// Convert degrees to radians.
var radians = this.mask.effects.rotate * (Math.PI / 180);
// Move registration point to the center of the canvas.
this.context.translate(this.canvas.width / 2, this.canvas.height / 2);
// Apply rotation.
this.context.rotate(radians);
// Move registration point back to the top left corner of canvas.
this.context.translate(-this.canvas.width / 2, -this.canvas.height / 2);
};

Let’s have a look at the slideshow controller.

让我们看一下幻灯片控制器。

At this point we have PrismSlider.js which can be instantiated and it will generate a canvas element, load the images and render both, mask and slide.

至此,我们可以实例化PrismSlider.js,它将生成一个canvas元素,加载图像并渲染蒙版和幻灯片。

To keep things clean we’ll add another script which we’ll use as main controller to give instructions to the PrismSlider.

为了保持整洁,我们将添加另一个脚本,该脚本将用作主控制器来向PrismSlider提供指令。

The code necessary for this is in slideshow.js. Let’s take a look at the configuration variables:

为此所需的代码在slideshow.js中。 让我们看一下配置变量:

/**
* Enum navigation classes, attributes and
* provide navigation DOM element container.
*/
var navigation = {
selector: '.navigation',
element: null,
bullet: 'li',
attrs: {
active: 'active',
index: 'data-index'
}
};/**
* Enum main element, sizes and provide
* main DOM element container.
* @type {Object}
*/
var container = {
selector: '.container',
element: null,
sizes: {
w: 1200,
h: 780
}
};/**
* Set of images to be used.
* @type {Array}
*/
var slides = [
'img/shoreditch-a.jpg',
'img/shoreditch-b.jpg',
'img/shoreditch-c.jpg',
'img/graffiti-a.jpg',
'img/graffiti-b.jpg',
'img/graffiti-c.jpg'
];/**
* Set of masks with related effects.
* @type {Array}
*/
var masks = [
{
source: 'img/masks/cube-a.svg',
effects: {
flip: 'Y',
rotate: 167 // degrees
}
},
{
source: 'img/masks/cube-b.svg',
effects: {
flip: 'X',
rotate: 90 // degrees
}
},
{
source: 'img/masks/cube-c.svg',
effects: {
flip: false,
rotate: 13 // degrees
}
}
];/**
* Set global easing.
* @type {Function(currentTime)}
*/
var easing = Easing.easeInOutQuint;/**
* Set global duration.
* @type {Number}
*/
var duration = 2000;/**
* Container for PrismSlider instances.
* @type {Object}
*/
var instances = {};

Notice the the last “instances” variable: it’s an empty object that we’ll use as a ‘container’ in order to keep a reference to each canvas, or better, to the PrismSlider instance and its methods.

注意最后一个“ instances”变量:这是一个空对象,我们将其用作“容器”,以便保留对每个画布或更佳的PrismSlider实例及其方法的引用。

In the init function steps are the following:

在init函数中,步骤如下:

/**
* Init.
*/
function init() {getContainer_();initSlider_();initPrism_();addNavigation_();addEvents_();
}/**
* Get main container element, and store in container element.
*/
function getContainer_() {
container.element = document.querySelector(container.selector);
}/**
* Init Slides.
* Create and initialise main background slider (first layer).
* Since we'll use this as main slider no mask is given.
*/
function initSlider_() {instances.slider = new PrismSlider({
container: container,
slides: slides,
mask: false,
duration: duration,
easing: easing
});// Initialise instance.
instances.slider.init();
}/**
* Init Masks.
* Loop masks variable and create a new layer for each mask object.
*/
function initPrism_() {masks.forEach(function(mask, i) {
// Generate reference name.
var name = 'mask_' + i;instances[name] = new PrismSlider({
container: container,
slides: slides,
mask: mask, // Here is the mask object.
duration: duration,
easing: easing
});// Initialise instance.
instances[name].init();
});
}/**
* Add Navigation.
* Create a new bullet for each slide and add it to navigation (ul)
* with data-index reference.
*/
function addNavigation_() {
// Store navigation element.
navigation.element = document.querySelector(navigation.selector);slides.forEach(function(slide, i) {var bullet = document.createElement(navigation.bullet);bullet.setAttribute(navigation.attrs.index, i);// When it's first bullet set class as active.
if (i === 0) bullet.className = navigation.attrs.active;navigation.element.appendChild(bullet);
});
}/**
* Add Events.
* Bind click on bullets.
*/
function addEvents_() {
...
}

In initSlider we create a new PrismSlider instance with mask set to false in order to have a full background layer.

initSlider中,我们创建一个新的PrismSlider实例,并将其mask设置为false ,以便具有完整的背景层。

In initPrism we loop through the masks array defined above and for each mask we create a new instance and pass the mask parameters.

initPrism中,我们遍历上面定义的masks数组,并为每个mask创建一个新实例并传递mask参数。

Now the only thing left to do is the animation. When a click on a navigation bullet is captured, the function slideAllTo is called:

现在剩下要做的就是动画。 捕获对导航项目符号的单击后,将调用函数slideAllTo

/**
* Add Events.
* Bind click on bullets.
*/
function addEvents_() {
// Detect click on navigation elment (ul).
navigation.element.addEventListener('click', function(e) {// Get clicked element.
var bullet = e.target;// Detect if the clicked element is actually a bullet (li).
var isBullet = bullet.nodeName === navigation.bullet.toUpperCase();// Check bullet and prevent action if animation is in progress.
if (isBullet && !instances.slider.isAnimated) {
// Remove active class from all bullets.
for (var i = 0; i < navigation.element.childNodes.length; i++) {
navigation.element.childNodes[i].className = '';
}
// Add active class to clicked bullet.
bullet.className = navigation.attrs.active;// Get index from data attribute and convert string to number.
var index = Number(bullet.getAttribute(navigation.attrs.index));// Call slideAllTo method with index.
slideAllTo_(index);
}});
}/**
* Call slideTo method of each instance.
* In order to sync sliding of all layers we'll loop through the
* instances object and call the slideTo method for each instance.
* @param {Number} index The index of the destination slide.
*/
function slideAllTo_(index) {
// Loop PrismSlider instances.
for (var key in instances) {
if (instances.hasOwnProperty(key)) {
// Call slideTo for current instance.
instances[key].slideTo(index);
}
}
}

As described in the comment, slideAllTo will loop through the instances and call the PrismSlider.prototype.slideTo method.

如评论中所述, slideAllTo将遍历实例并调用PrismSlider.prototype.slideTo方法。

So let's add this method to PrismSlider.js together with animate_ and ticker_ which are used to run the sliding:

因此,让我们将此方法与animate_ticker_一起添加到PrismSlider.js中,这些方法用于运行滑动:

/**
* Slide To.
* @param {Number} index The destination slide index.
*/
PrismSlider.prototype.slideTo = function(index) {
// Prevent when animation is in progress or if same bullet is clicked.
if (this.isAnimated || index === this.slidesIndex) return;// Store current (start) index.
this.prevSlidesIndex = this.slidesIndex;
// Set destination (end) index.
this.slidesIndex = index;// Calculate how many slides between current (start) and destination (end).
var indexOffset = (this.prevSlidesIndex - this.slidesIndex) * -1;
// Store offset always converted to positive number.
this.indexOffset = (indexOffset > 0) ? indexOffset : indexOffset * -1;// Kickstart animation.
this.animate_();
};

In the above method the key steps are when we update the indexes and when we calculate how many slides we need to animate with indexOffset.

在上述方法中,关键步骤是何时更新索引以及何时计算需要使用indexOffset进行动画处理的幻灯片

The lasts two methods are animate which simply calculate the end time by adding the duration to Date.now(), and ticker, which is basically the method that we call with requestAnimationFrame.

的持续两种方法是有生命的,其简单地通过将所述持续时间Date.now(计算结束时间),以及断续器,它基本上是,我们称之为使用requestAnimationFrame的方法。

/**
* Animate.
*/
PrismSlider.prototype.animate_ = function() {// Calculate end time.
var end = Date.now() + this.duration;// Mark animation as in progress.
this.isAnimated = true;
// Kickstart frames ticker.
this.ticker_(end);
};/**
* Ticker called for each frame of the animation.
* @param {Number} end The end time of the animation.
*/
PrismSlider.prototype.ticker_ = function(end) {// Start time.
var now = Date.now();
// Update time left in the animation.
var remaining = end - now;// Retrieve easing and multiply for number of slides between stars
// and end, in order to jump through N slides in one ease.
var easing = this.easing(remaining / this.duration) * this.indexOffset;var i, progress, slide;// Select sliding direction.
if (this.slidesIndex > this.prevSlidesIndex) {// Sliding forward.
progress = this.slidesIndex - easing;// Loop offset and render slides from start to end.
for (i = 0; i <= this.indexOffset; i++) {
slide = this.slidesIndex - i;
this.renderSlide_(slide, progress);
}} else {// Sliding backward.
progress = this.slidesIndex + easing;// Loop offset and render slides from start to end.
for (i = 0; i <= this.indexOffset; i++) {
slide = this.slidesIndex + i;
this.renderSlide_(slide, progress);
}
}// Under 50 milliseconds reset and stop.
if (remaining < 50) {
// Set default value.
this.indexOffset = 1;
// Make sure slide is perfectly aligned.
this.renderSlide_(this.slidesIndex);
// Mark animation as finished.
this.isAnimated = false;
// Stop.
return;
}// Kickstart rAF with updated end.
window.requestAnimationFrame(this.ticker_.bind(this, end));
};

结论 (Conclusion)

I hope you enjoyed this tutorial and find it useful!

我希望您喜欢本教程并发现它有用!

Please note that in a real life case it would be better to preload the images directly with JavaScript by extending the code presented above in order to accomplish a more dynamic optimization.

请注意,在现实生活中,最好通过扩展上面提供的代码直接用JavaScript预加载图像,以实现更动态的优化。

Download the full source and have a look at the files; everything is well documented and, I hope, pretty straightforward to understand and adapt to your needs.

下载完整的源代码并查看文件; 一切内容都有据可查,并且我希望它非常容易理解和适应您的需求。

翻译自: https://tympanus.net/codrops/2015/03/31/prism-effect-slider-canvas/

棱镜计划

棱镜计划_棱镜效果滑块与画布相关推荐

  1. 棱镜计划_棱镜vs类型

    棱镜计划 Working with databases is an unavoidable part of being a backend developer. 作为后端开发人员,使用数据库是不可避免 ...

  2. CSS练习_云层效果

    之前练习的一个网页,只利用css完成,算是一个小demo吧 想要的效果: 云层无限滚动 背景天空会由白变黑再变白 云层滚动有视差(速度不同) 效果图: 代码: <!DOCTYPE html> ...

  3. [51单片机] TFT2.4彩屏3 [自制动画效果-滑块+吊钩]

    >_<:引脚和前面几个连接一样,这里做了一个实用的动画效果,模拟起重机的2维视图.  9325tp.h  9325tp.c 1 #include<reg52.h> 2 #inc ...

  4. android 加载动画效果_这效果炸了,网易云音乐“宇宙尘埃”特效

    本文作者 作者:Mlx 链接: https://juejin.im/post/6871049441546567688 本文由作者授权发布. 1前言 前段时间,女朋友用网易云音乐的时候看到一个宇宙尘埃特 ...

  5. h5画布动画_如何使用CCapture保存画布动画

    h5画布动画 by Ibby EL-Serafy 由Ibby EL-Serafy 如何使用CCapture保存画布动画 (How to save canvas animations with CCap ...

  6. python打字机效果_打字效果动画的4种实现方法(超简单)

    方法一(纯css实现): html代码: 打字动画打字动画打字动画 css样式: .typing{ font-size: 1rem; padding-top: 6%; margin-bottom: 5 ...

  7. 猫猫学IOS(四十)UI之核心动画_抖动效果_CAKeyframeAnimation

    猫猫分享,必须精品 原创文章,欢迎转载.转载请注明:翟乃玉的博客 地址:http://blog.csdn.net/u013357243?viewmode=contents 效果: 效果一: 效果二: ...

  8. au vst插件_失真效果音频插件

    iZotope Trash 2 Mac版是一款可以在苹果电脑MAC OS平台上使用的具有64bit处理制造失真效果的音频插件.iZotope Trash 2 可以对任何音频文件进行失真效果的处理,现为 ...

  9. 律师如何加强自身的计算机文化教育网,提高_计算机文化基础_教学效果的几点心得.pdf...

    Science & Technology Vision 科技·探索·争鸣 科技视界 高校科技 提高<计算机文化基础>教学效果的几点心得 王继敏 河南科技学院信息工程学院 河南新乡 ...

最新文章

  1. 2022-2028年中国醋酸行业投资分析及前景预测报告
  2. 遍历Treeview每个节点并初始化(C#)
  3. ThinkPHP的标签制作
  4. [转]PHP用mysql数据库存储session
  5. PowerDesigner提示This data item is already used in a primary identifier.的处理
  6. .Net Core 之 Ubuntu 14.04 部署过程
  7. 欢乐纪中A组周六赛【2019.5.18】
  8. Java并发篇_乐观锁与悲观锁
  9. HTMLCSS--使用CSS完成页面布局及排版(附案例代码)
  10. MySQL架构设计相关的方式方法和软件介绍
  11. python输入一个假分数_腾讯内容开放平台
  12. linux服务器搭建教程c,Linux服务器上搭建web项目环境
  13. 【图像配准】基于matlab GUI SIFT图像配准拼接【含Matlab源码 854期】
  14. python简明教程3.0_Python 简明教程 --- 0,前言
  15. excel计算机课程表,Excel如何制作课程表
  16. 关于react、vue的一些问题
  17. 联想拯救者y空间_锐龙H真给劲儿 联想拯救者R7000 2020首测
  18. 解决“javac不是内部或外部命令,也不是可运行的程序”问题
  19. No power supply specified for netVCC in Power Rail Confiquration.
  20. 重装Intel核显后,设备管理器英特尔显卡属性这里报告了一个未启动设备(igfx)

热门文章

  1. 多种查看系统时间的方法
  2. 学习记录677@项目管理之配置管理案例
  3. html中radio标签
  4. 柏拉图笔下的大西洲绝不可能是克里特岛的米诺文明
  5. Dimitra,以区块链的方式为农业深度赋能
  6. java timespan 格式化_将Timespan转换为1天到Datetime时出错
  7. oracle常见错误代号
  8. 输出“人生苦短,我学Python”
  9. 一篇文章让你对sysfs文件及属性了解透彻【Linux内核】
  10. 在微型计算机中 常见到,在微型计算机中,常见到的EGA、VGA等是指()。