區塊鏈火熱,做爲程序猿的我,固然也不能袖手旁觀,一位資深技術開發朋友曾笑說:這是屌絲程序猿改變命運爲數很少的機會之一。因此,從今天開始,就要步入區塊鏈的開發大潮中。
語言:使用 node.js
開發該項目
大概流程:css
合約代碼編寫(Solidity)-> 合約編譯(solc)-> 合約部署(web3)html
sudo npm install -g ganache-cli
運行:前端
ganache-cli
輸出:node
➜ ~ ganache-cli Ganache CLI v6.1.0 (ganache-core: 2.1.0) Available Accounts ================== (0) 0x2c0e940b0732a3b49cb7dccc26e14cb4801dd1c3 (1) 0x65afabcf1fdb19ef88f8433af90136de56e7e412 (2) 0x65111c1fa94e15e8e3bdedb466004f67d6b46bab (3) 0xfa44030a4216193d19a811267528e86cf1851e48 (4) 0xc29473dca76a2ebbb8b1badf6a8093c11b56ea84 (5) 0x06e55addeef67a46015e2790be1ada1deb3c9c70 (6) 0xc1ec7f3d08692d0bdd70d6ab3d5701f22f53a521 (7) 0x42e52cbb5e226ef8c2c9bf54737b87ccf94ebb08 (8) 0x8cebfdb948266022d2499118b0989b290d146d4c (9) 0x17b791127c57dff3eb31cc203e404536ef7e0ba7 Private Keys ================== (0) 3bb508f1c2c35083f7d69466830067c6582e4464ba61daffc947bb1aa98618e9 (1) fc06e722c10cd80b1b5b43355f81363dcbe6dcc8d3c59387f69c68ce99f36c53 (2) 07f37ed746ba88da289eaa780d6155d9fee456106d85169ad92a526c22192695 (3) 2619b581c083d20ff84db2688f4a9d836206ee37e993bc8cb1e089ad68c8673f (4) c3f61de226b5d5c06cb941f93a2a3ec321dabc53a8fb68bee64d3aed5bc130e6 (5) f86e7b7e7a9cf7532004694cb22997ac521567b7c8e480dbee23e426ed787234 (6) 2035f13d8d64109f21e4eb32970e5934cddcd27bc55439634f49d4479c7abe77 (7) 3395049c4f8749b17e154c47199fa42ce538ed051b6240afc55f49d30406a4f0 (8) 976f56be1b1cd9f5c420a3fdb71eb3a8c3875a7bd3fba20c342389ba97b0a165 (9) a2a7a190ee76cdb0675b8af773fba55187ff4a0fc6c1e1021e717d19e0d591ee HD Wallet ================== Mnemonic: result casino this poverty sleep joy toy sort onion spider bind evolve Base HD Path: m/44'/60'/0'/0/{account_index} Listening on localhost:8545
ganache
默認會自動建立 10 個帳戶,每一個帳戶有 100 個以太幣(ETH:Ether)。 能夠把帳戶視爲銀行帳戶,以太幣就是以太坊生態系統中的貨幣。git
面輸出的最後一句話,描述了節點仿真器的監聽地址和端口爲localhost:8545
,在使用 web3.js
時,須要傳入這個地址來告訴web3js庫應當鏈接到哪個節點。web
咱們使用 Solidity 語言來編寫合約。若是你熟悉面向對象的開發和JavaScript,那麼學習Solidity 應該很是簡單。能夠將合約類比於OOP的類:合約中的屬性
用來聲明合約的狀態
,而合約中的方法
則提供修改狀態的訪問接口。算法
重點:數據庫
從最基本的開始入手:npm
Solidity
的代碼都包裹在合約裏面. 一份合約就是以太應幣應用的基本模塊, 全部的變量和函數都屬於一份合約, 它是你全部應用的起點.編程
一份名爲 HelloWorld
的空合約以下:
contract HelloWorld { }
全部的 Solidity 源碼都必須冠以 "version pragma" — 標明 Solidity 編譯器的版本. 以免未來新的編譯器可能破壞你的代碼。
例如: pragma solidity ^0.4.19;
(當前 Solidity 的最新版本是 0.4.19).
綜上所述, 下面就是一個最基本的合約 — 每次創建一個新的項目時的第一段代碼:
contract.sol
合約文件
// 1. 這裏寫版本指令 pragma solidity ^0.4.19; // 2. 這裏創建智能合約 contract HelloWorld { }
爲了創建咱們的殭屍部隊, 讓咱們先創建一個基礎合約,稱爲 ZombieFactory
。
0.4.19
,咱們的合約基於這個版本的編譯器。ZombieFactory
。Contract.sol
pragma solidity ^0.4.19; // 1. 這裏寫版本指令 // 2. 這裏創建智能合約 contract ZombieFactory { }
狀態變量
是被永久地保存在合約中。也就是說它們被寫入以太幣區塊鏈中. 想象成寫入一個數據庫。
示例:
contract Example { // 這個無符號整數將會永久的被保存在區塊鏈中 uint myUnsignedInteger = 100; }
在上面的例子中,定義 myUnsignedInteger 爲 uint 類型,並賦值100。
uint
無符號數據類型, 指其值不能是負數,對於有符號的整數存在名爲 int
的數據類型。
注: Solidity中, uint
其實是 uint256
代名詞, 一個256位的無符號整數。你也能夠定義位數少的uints — uint8
, uint16
, uint32
, 等…… 但通常來說你願意使用簡單的 uint
, 除非在某些特殊狀況下,這咱們後面會講。
咱們的殭屍DNA將由一個十六位數字組成。
dnaDigits
爲 uint
數據類型, 並賦值 16
。Contract.sol
pragma solidity ^0.4.19; // 1. 這裏寫版本指令 // 2. 這裏創建智能合約 contract ZombieFactory { // 3.定義 dnaDigits 爲 uint 數據類型, 並賦值 16 uint dnaDigits = 16; }
在 Solidity 中,數學運算很直觀明瞭,與其它程序設計語言相同:
Solidity 還支持 乘方操做
(如:x 的 y次方) // 例如: 5 ** 2 = 25
uint x = 5 ** 2; // equal to 5^2 = 25
爲了保證咱們的殭屍的DNA只含有16個字符,咱們先造一個uint數據,讓它等於10^16。這樣一來之後咱們能夠用模運算符 % 把一個整數變成16位。
uint
類型的變量,名字叫dnaModulus
, 令其等於 10
的 dnaDigits
次方。Contract.sol
pragma solidity ^0.4.19; // 1. 這裏寫版本指令 // 2. 這裏創建智能合約 contract ZombieFactory { // 3. 定義 dnaDigits 爲 uint 數據類型, 並賦值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; }
有時你須要更復雜的數據類型,Solidity 提供了 結構體:
struct Person { uint age; string name; }
結構體容許你生成一個更復雜的數據類型,它有多個屬性。
注:咱們剛剛引進了一個新類型,string
。 字符串用於保存任意長度的UTF-8
編碼數據。 如:string greeting = "Hello world!"
。
在咱們的程序中,咱們將建立一些殭屍!每一個殭屍將擁有多個屬性,因此這是一個展現結構體的完美例子。
struct
命名爲 Zombie
。Zombie
結構體有兩個屬性: name
(類型爲 string
), 和 dna
(類型爲 uint
)。Contract.sol
// 1. 這裏寫版本指令 pragma solidity ^0.4.19; // 2. 這裏創建智能合約 contract ZombieFactory { // 3. 定義 dnaDigits 爲 uint 數據類型, 並賦值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.結構體定義 struct Zombie { string name; uint dna; } }
若是你想創建一個集合,能夠用 數組
這樣的數據類型. Solidity 支持兩種數組: 靜態
數組和動態
數組:
// 固定長度爲2的靜態數組: uint[2] fixedArray; // 固定長度爲5的string類型的靜態數組: string[5] stringArray; // 動態數組,長度不固定,能夠動態添加元素: uint[] dynamicArray;
你也能夠創建一個 結構體類型的數組 例如,上一節提到的 Person:
Person[] people; // dynamic Array, we can keep adding to it
記住:狀態變量被永久保存在區塊鏈中。因此在你的合約中建立動態數組來保存成結構的數據是很是有意義的。
你能夠定義 public
數組, Solidity 會自動建立 getter
方法. 語法以下:
Person[] public people;
其它的合約能夠從這個數組讀取數據(但不能寫入數據),因此這在合約中是一個有用的保存公共數據的模式。
爲了把一個殭屍部隊保存在咱們的APP裏,而且可以讓其它APP看到這些殭屍,咱們須要一個公共數組。
Zombie
的結構體數組,用 public
修飾,命名爲:zombies
.Contract.sol
// 1. 這裏寫版本指令 pragma solidity ^0.4.19; // 2. 這裏創建智能合約 contract ZombieFactory { // 3. 定義 dnaDigits 爲 uint 數據類型, 並賦值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.結構體定義 struct Zombie { string name; uint dna; } // 6.數組類型爲結構體的公共數組 Zombie[] public zombies; }
在 Solidity 中函數定義的句法以下:
function eatHamburgers(string _name, uint _amount) { }
這是一個名爲 eatHamburgers
的函數,它接受兩個參數:一個 string
類型的 和 一個 uint
類型的。如今函數內部仍是空的。
注:: 習慣上函數裏的變量都是以(
_
)開頭 (但不是硬性規定) 以區別全局變量。咱們整個教程都會沿用這個習慣。
咱們的函數定義以下:
eatHamburgers("vitalik", 100);
在咱們的應用裏,咱們要能建立一些殭屍,讓咱們寫一個函數作這件事吧!
createZombie
。 它有兩個參數: _name
(類型爲string
), 和 _dna
(類型爲uint
)。暫時讓函數空着——咱們在後面會增長內容。Contract.sol
// 1. 這裏寫版本指令 pragma solidity ^0.4.19; // 2. 這裏創建智能合約 contract ZombieFactory { // 3. 定義 dnaDigits 爲 uint 數據類型, 並賦值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.結構體定義 struct Zombie { string name; uint dna; } // 6.數組類型爲結構體的公共數組 Zombie[] public zombies; // 7.建立函數 function createZombie(string _name, uint _dna){ } }
還記得上個例子中的 Person 結構嗎?
struct Person { uint age; string name; } Person[] public people;
如今咱們學習建立新的 Person
結構,而後把它加入到名爲 people
的數組中.
// 建立一個新的Person: Person satoshi = Person(172, "Satoshi"); // 將新建立的satoshi添加進people數組: people.push(satoshi);
你也能夠兩步並一步,用一行代碼更簡潔:
people.push(Person(16, "Vitalik"));
注:array.push()
在數組的尾部
加入新元素 ,因此元素在數組中的順序就是咱們添加的順序, 如:
uint[] numbers; numbers.push(5); numbers.push(10); numbers.push(15); // numbers is now equal to [5, 10, 15]
讓咱們建立名爲createZombie的函數來作點兒什麼吧。
Zombie
, 而後把它加入 zombies
數組中。 新建立的殭屍的 name
和 dna
,來自於函數的參數。Contract.sol
// 1. 這裏寫版本指令 pragma solidity ^0.4.19; // 2. 這裏創建智能合約 contract ZombieFactory { // 3. 定義 dnaDigits 爲 uint 數據類型, 並賦值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.結構體定義 struct Zombie { string name; uint dna; } // 6.數組類型爲結構體的公共數組 Zombie[] public zombies; // 7.建立函數 function createZombie(string _name, uint _dna){ // 8.使用結構體和數組(初始化全局數組) zombies.push(Zombie(_name, _dna)); } }
Solidity 定義的函數的屬性默認
爲公共
。 這就意味着任何一方 (或其它合約) 均可以調用你合約裏的函數。
顯然,不是何時都須要這樣,並且這樣的合約易於受到攻擊。 因此將本身的函數定義爲私有
是一個好的編程習慣,只有當你須要外部世界調用它時纔將它設置爲公共
。
如何定義一個私有的函數呢?
uint[] numbers; function _addToArray(uint _number) private { numbers.push(_number); }
這意味着只有咱們合約中的其它函數纔可以調用這個函數,給 numbers
數組添加新成員。
能夠看到,在函數名字後面使用關鍵字 private
便可。和函數的參數相似,私有函數的名字用(_
)起始。
咱們合約的函數 createZombie
的默認屬性是公共的,這意味着任何一方均可以調用它去建立一個殭屍。 我們來把它變成私有吧!
Contract.sol
// 1. 這裏寫版本指令 pragma solidity ^0.4.19; // 2. 這裏創建智能合約 contract ZombieFactory { // 3. 定義 dnaDigits 爲 uint 數據類型, 並賦值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.結構體定義 struct Zombie { string name; uint dna; } // 6.數組類型爲結構體的公共數組 Zombie[] public zombies; /* // 7.建立函數 function createZombie(string _name, uint _dna){ // 8.使用結構體和數組(初始化全局數組) zombies.push(Zombie(_name, _dna)); } */ // 7.建立函數(改成私有方法) function _createZombie(string _name, uint _dna) private { // 8.使用結構體和數組(初始化全局數組) zombies.push(Zombie(_name, _dna)); } }
本節中咱們將學習函數的返回值和修飾符。
要想函數返回一個數值,按以下定義:
string greeting = "What's up dog"; function sayHello() public returns (string) { return greeting; }
Solidity 裏,函數的定義裏可包含返回值的數據類型(如本例中 string
)。
上面的函數實際上沒有改變 Solidity 裏的狀態,即,它沒有改變任何值或者寫任何東西。
這種狀況下咱們能夠把函數定義爲 view
, 意味着它只能讀取數據不能更改數據:
function sayHello() public view returns (string) {}
Solidity 還支持 pure
函數, 代表這個函數甚至都不訪問應用裏的數據,例如:
function _multiply(uint a, uint b) private pure returns (uint) { return a * b; }
這個函數甚至都不讀取應用裏的狀態 — 它的返回值徹底取決於它的輸入參數,在這種狀況下咱們把函數定義爲 pure
.
注:可能很難記住什麼時候把函數標記爲
pure/view
。 幸運的是, Solidity 編輯器會給出提示,提醒你使用這些修飾符。
咱們想創建一個幫助函數,它根據一個字符串隨機生成一個DNA數據。
private
函數,命名爲 _generateRandomDna
。它只接收一個輸入變量 _str
(類型 string
), 返回一個 uint
類型的數值。view
。Contract.sol
// 1. 這裏寫版本指令 pragma solidity ^0.4.19; // 2. 這裏創建智能合約 contract ZombieFactory { // 3. 定義 dnaDigits 爲 uint 數據類型, 並賦值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.結構體定義 struct Zombie { string name; uint dna; } // 6.數組類型爲結構體的公共數組 Zombie[] public zombies; /* // 7.建立函數 function createZombie(string _name, uint _dna){ // 8.使用結構體和數組(初始化全局數組) zombies.push(Zombie(_name, _dna)); } */ // 7.建立函數(改成私有方法) function _createZombie(string _name, uint _dna) private { // 8.使用結構體和數組(初始化全局數組) zombies.push(Zombie(_name, _dna)); } // 9.函數修飾符 private, view, returns 返回值 function _generateRandomDna(string _str) private view returns (uint){ } }
如何讓 _generateRandomDna
函數返回一個全(半) 隨機的 uint
?
Ethereum 內部有一個散列函數keccak256
,它用了SHA3版本。一個散列函數基本上就是把一個字符串轉換爲一個256位的16進制數字。字符串的一個微小變化會引發散列數據極大變化。
這在 Ethereum 中有不少應用,可是如今咱們只是用它造一個僞隨機數。
示例:
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5 keccak256("aaaab"); //b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9 keccak256("aaaac");
顯而易見,輸入字符串只改變了一個字母,輸出就已經天壤之別了。
注: 在區塊鏈中安全地產生一個隨機數是一個很難的問題, 本例的方法不安全,可是在咱們的Zombie DNA算法裏不是那麼重要,已經很好地知足咱們的須要了。
有時你須要變換數據類型。例如:
uint8 a = 5; uint b = 6; // 將會拋出錯誤,由於 a * b 返回 uint, 而不是 uint8: uint8 c = a * b; // 咱們須要將 b 轉換爲 uint8: uint8 c = a * uint8(b);
上面,a * b
返回類型是uint
, 可是當咱們嘗試用uint8
類型接收時, 就會形成潛在的錯誤。若是把它的數據類型轉換爲uint8
, 就能夠了,編譯器也不會出錯。
給 _generateRandomDna
函數添加代碼! 它應該完成以下功能:
_str
的 keccak256
散列值生成一個僞隨機十六進制數,類型轉換爲 uint
, 最後保存在類型爲 uint
名爲 rand
的變量中。return
上面計算的數值對 dnaModulus
求餘數(%
)。Contract.sol
// 1. 這裏寫版本指令 pragma solidity ^0.4.19; // 2. 這裏創建智能合約 contract ZombieFactory { // 3. 定義 dnaDigits 爲 uint 數據類型, 並賦值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.結構體定義 struct Zombie { string name; uint dna; } // 6.數組類型爲結構體的公共數組 Zombie[] public zombies; /* // 7.建立函數 function createZombie(string _name, uint _dna){ // 8.使用結構體和數組(初始化全局數組) zombies.push(Zombie(_name, _dna)); } */ // 7.建立函數(改成私有方法) function _createZombie(string _name, uint _dna) private { // 8.使用結構體和數組(初始化全局數組) zombies.push(Zombie(_name, _dna)); } // 9.函數修飾符 private, view, returns 返回值 function _generateRandomDna(string _str) private view returns (uint){ // 10.散列並取模 uint rand = uint(keccak256(_str)); // 注意這裏須要將string類型轉爲uint類型 return rand % dnaModulus; } } }
咱們就快完成咱們的隨機殭屍製造器了,來寫一個公共的函數把全部的部件鏈接起來。
寫一個公共函數,它有一個參數,用來接收殭屍的名字,以後用它生成殭屍的DNA。
public
函數,命名爲createRandomZombie
. 它將被傳入一個變量 _name
(數據類型是 string
)。 (注: 定義公共函數 public
和定義一個私有 private
函數的作法同樣)。_generateRandomDna
函數,傳入 _name
參數, 結果保存在一個類型爲 uint
的變量裏,命名爲 randDna
。_createZombie
函數, 傳入參數: _name
和 randDna
。}
)。Contract.sol
// 1. 這裏寫版本指令 pragma solidity ^0.4.19; // 2. 這裏創建智能合約 contract ZombieFactory { // 3. 定義 dnaDigits 爲 uint 數據類型, 並賦值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.結構體定義 struct Zombie { string name; uint dna; } // 6.數組類型爲結構體的公共數組 Zombie[] public zombies; /* // 7.建立函數 function createZombie(string _name, uint _dna){ // 8.使用結構體和數組(初始化全局數組) zombies.push(Zombie(_name, _dna)); } */ // 7.建立函數(改成私有方法) function _createZombie(string _name, uint _dna) private { // 8.使用結構體和數組(初始化全局數組) zombies.push(Zombie(_name, _dna)); } // 9.函數修飾符 private, view, returns 返回值 function _generateRandomDna(string _str) private view returns (uint){ // 10.散列並取模 uint rand = uint(keccak256(_str)); // 注意這裏須要將string類型轉爲uint類型 return rand % dnaModulus; } // 十一、事件 function createRandomZombie(string _name) public { uint randDna = _generateRandomDna(_name); _createZombie(_name, randDna); } }
咱們的合約幾乎就要完成了!讓咱們加上一個事件.
事件
是合約和區塊鏈通信的一種機制。你的前端應用「監聽」某些事件,並作出反應。
示例:
// 這裏創建事件 event IntegersAdded(uint x, uint y, uint result); function add(uint _x, uint _y) public { uint result = _x + _y; //觸發事件,通知app IntegersAdded(_x, _y, result); return result; }
你的 app 前端能夠監聽這個事件。JavaScript 實現以下:
YourContract.IntegersAdded(function(error, result) { // 幹些事 }
咱們想每當一個殭屍創造出來時,咱們的前端都能監聽到這個事件,並將它顯示出來。
事件
叫作 NewZombie
。 它有3個參數: zombieId (uint)
, name (string)
, 和 dna (uint)
。_createZombie
函數使得當新殭屍造出來並加入zombies
數組後,生成事件NewZombie
。id
。 array.push()
返回數組的長度
類型是uint
- 由於數組的第一個元素的索引是 0
, array.push() - 1
將是咱們加入的殭屍的索引。 zombies.push() - 1
就是 id
,數據類型是 uint
。在下一行中你能夠把它用到 NewZombie
事件中。Contract.sol
// 1. 這裏寫版本指令 pragma solidity ^0.4.19; // 2. 這裏創建智能合約 contract ZombieFactory { // 12.這裏創建事件 event NewZombie(uint zombieId, string name, uint dna); // 3. 定義 dnaDigits 爲 uint 數據類型, 並賦值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.結構體定義 struct Zombie { string name; uint dna; } // 6.數組類型爲結構體的公共數組 Zombie[] public zombies; /* // 7.建立函數 function createZombie(string _name, uint _dna){ // 8.使用結構體和數組(初始化全局數組) zombies.push(Zombie(_name, _dna)); } */ // 7.建立函數(改成私有方法) function _createZombie(string _name, uint _dna) private { // 8.使用結構體和數組(初始化全局數組) // zombies.push(Zombie(_name, _dna)); // 十二、數組長度減一就是當前的數組ID uint id = zombies.push(Zombie(_name, _dna)) - 1; // 十二、這裏觸發事件 NewZombie(id, _name, _dna); } // 9.函數修飾符 private, view, returns 返回值 function _generateRandomDna(string _str) private view returns (uint){ // 10.散列並取模 uint rand = uint(keccak256(_str)); // 注意這裏須要將string類型轉爲uint類型 return rand % dnaModulus; } // 十一、綜合函數 function createRandomZombie(string _name) public { uint randDna = _generateRandomDna(_name); _createZombie(_name, randDna); } }
咱們的 Solidity
合約完工了! 如今咱們要寫一段 JavaScript
前端代碼來調用這個合約。
以太坊有一個 JavaScript 庫,名爲Web3.js
。
在後面的課程裏,咱們會進一步地教你如何安裝一個合約,如何設置Web3.js。 可是如今咱們經過一段代碼來了解 Web3.js 是如何和咱們發佈的合約交互的吧。
若是下面的代碼你不能全都理解,不用擔憂。
// 下面是調用合約的方式: var abi = /* abi是由編譯器生成的 */ var ZombieFactoryContract = web3.eth.contract(abi) var contractAddress = /* 發佈以後在以太坊上生成的合約地址 */ var ZombieFactory = ZombieFactoryContract.at(contractAddress) // `ZombieFactory` 能訪問公共的函數以及事件 // 某個監聽文本輸入的監聽器: $("#ourButton").click(function(e) { var name = $("#nameInput").val() //調用合約的 `createRandomZombie` 函數: ZombieFactory.createRandomZombie(name) }) // 監聽 `NewZombie` 事件, 而且更新UI var event = ZombieFactory.NewZombie(function(error, result) { if (error) return generateZombie(result.zombieId, result.name, result.dna) }) // 獲取 Zombie 的 dna, 更新圖像 function generateZombie(id, name, dna) { let dnaStr = String(dna) // 若是dna少於16位,在它前面用0補上 while (dnaStr.length < 16) dnaStr = "0" + dnaStr let zombieDetails = { // 前兩位數構成頭部.咱們可能有7種頭部, 因此 % 7 // 獲得的數在0-6,再加上1,數的範圍變成1-7 // 經過這樣計算: headChoice: dnaStr.substring(0, 2) % 7 + 1, // 咱們獲得的圖片名稱從head1.png 到 head7.png // 接下來的兩位數構成眼睛, 眼睛變化就對11取模: eyeChoice: dnaStr.substring(2, 4) % 11 + 1, // 再接下來的兩位數構成衣服,衣服變化就對6取模: shirtChoice: dnaStr.substring(4, 6) % 6 + 1, //最後6位控制顏色. 用css選擇器: hue-rotate來更新 // 360度: skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360), eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360), clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360), zombieName: name, zombieDescription: "A Level 1 CryptoZombie", } return zombieDetails }
咱們的 JavaScript 所作的就是獲取由zombieDetails
產生的數據, 而且利用瀏覽器裏的 JavaScript 神奇功能 (咱們用 Vue.js
),置換出圖像以及使用CSS過濾器。在後面的課程中,你能夠看到所有的代碼。
Truffle
是一個DApp開發框架,它簡化了去中心化應用的構建和管理。
注:本教程是匯智網-以太坊DApp開發入門學習筆記,感興趣的同窗能夠去學習。