केस स्टडी - Technitone.com बनाना

Sean Middleditch
Sean Middleditch
टेक्नीटोन — एक वेब ऑडियो अनुभव.

Technitone.com, WebGL, कैनवस, वेब सॉकेट, CSS3, JavaScript, Flash, और Chrome के नए Web Audio API का एक फ़्यूज़न है.

इस लेख में हम प्रोडक्शन के हर पहलू के बारे में बात करेंगे. जैसे, प्लान, सर्वर, साउंड, विज़ुअल, और कुछ वर्कफ़्लो जिसे हम इंटरैक्टिव बनाने के लिए इस्तेमाल करते हैं. ज़्यादातर सेक्शन में कोड स्निपेट, डेमो, और डाउनलोड की गई फ़ाइल होती है. इस लेख के आखिर में, डाउनलोड करने का लिंक होगा, जहां उन सभी को एक ZIP फ़ाइल के तौर पर ऐक्सेस किया जा सकता है.

gskinner.com की प्रोडक्शन टीम.

पार्ट टाइम

हम gskinner.com पर ऑडियो इंजीनियर नहीं हैं, लेकिन हमें कोई चुनौती देने के लिए उकसाएं और हम एक योजना सोचें:

  • लोग ग्रिड पर टोन का इस्तेमाल करते हैं, ऐंड्रे के ToneMatrix से "प्रेरित"
  • टोन, सैंपल किए गए इंस्ट्रुमेंट, ड्रम किट या यहां तक कि लोगों की खुद की रिकॉर्डिंग के लिए भी इस्तेमाल किए जाते हैं
  • कनेक्ट किए गए कई उपयोगकर्ता एक साथ एक ही ग्रिड पर काम करते हैं
  • ...या खुद एक्सप्लोर करने के लिए अकेले मोड में जाएं
  • न्योते वाले सेशन से उपयोगकर्ता, बैंड को व्यवस्थित कर सकते हैं और अचानक से संगीत का आनंद ले सकते हैं

हम उपयोगकर्ताओं को एक ऐसे टूल पैनल की मदद से Web Audio API को एक्सप्लोर करने का अवसर देते हैं जो ऑडियो फ़िल्टर और उनकी टोन पर इफ़ेक्ट लागू करता है.

gskinner.com का Technitone

हम:

  • उपयोगकर्ताओं के कंपोज़िशन और इफ़ेक्ट को डेटा के तौर पर सेव करें और इसे सभी क्लाइंट के साथ सिंक करें
  • रंगों के कुछ विकल्प दें, ताकि वे शानदार गाने बना सकें
  • एक गैलरी बनाएं, ताकि लोग दूसरों के काम को सुन सकें, पसंद कर सकें या उसमें बदलाव कर सकें

हमने जाने-पहचाने ग्रिड रूपक के साथ काम कर लिया, इसे 3D स्पेस में फ़्लोट किया, कुछ लाइटिंग, टेक्सचर, और पार्टिकल इफ़ेक्ट जोड़े. साथ ही, इसे लचीले (या फ़ुलस्क्रीन) सीएसएस और JS से चलने वाले इंटरफ़ेस में रखा.

रोड ट्रिप

इंस्ट्रुमेंट, इफ़ेक्ट, और ग्रिड डेटा को एक साथ इकट्ठा किया जाता है और क्लाइंट पर क्रम से लगाया जाता है. इसके बाद, उसे हमारे कस्टम Node.js बैकएंड पर भेजा जाता है, ताकि कई उपयोगकर्ताओं को Socket.io सुविधा मिल सके. यह डेटा हर खिलाड़ी के योगदान के साथ क्लाइंट को वापस भेजा जाता है. यह डेटा मल्टी-यूज़र प्लेबैक के दौरान यूज़र इंटरफ़ेस (यूआई), सैंपल, और इफ़ेक्ट को रेंडर करने के ज़िम्मेदार सीएसएस, WebGL, और WebAudio लेयर के साथ बांटा जाता है.

सॉकेट के साथ रीयल-टाइम कम्यूनिकेशन, क्लाइंट पर JavaScript और सर्वर पर JavaScript को फ़ीड करता है.

टेक्नीटोन सर्वर डायग्राम

हम सर्वर के हर पहलू के लिए नोड का इस्तेमाल करते हैं. यह एक स्टैटिक वेब सर्वर और हमारा सॉकेट सर्वर है. एक्सप्रेशन में हमने इसका इस्तेमाल किया. यह पूरी तरह से नोड पर बना पूरा वेब सर्वर है. यह बड़े पैमाने पर इस्तेमाल किया जा सकने वाला है और इसमें अपनी ज़रूरत के हिसाब से बदलाव किया जा सकता है. साथ ही, यह सर्वर के लो-लेवल वाले पहलुओं को भी आपके लिए मैनेज करता है (जैसे Apache या Windows Server की तरह). इसके बाद, आपको डेवलपर के तौर पर सिर्फ़ अपना ऐप्लिकेशन बनाने पर ध्यान देना होगा.

मल्टी-उपयोगकर्ता डेमो (ठीक है, यह सिर्फ़ एक स्क्रीनशॉट है)

इस डेमो को किसी Node सर्वर पर चलाना ज़रूरी है. इस लेख में यह बताया गया है कि Node.js को इंस्टॉल करने, वेब सर्वर को कॉन्फ़िगर करने, और उसे स्थानीय तौर पर चलाने के बाद, डेमो कैसा दिखता है. जब भी कोई नया उपयोगकर्ता आपके डेमो इंस्टॉलेशन पर जाएगा, तो एक नया ग्रिड जोड़ दिया जाएगा और सभी का काम एक-दूसरे को दिखेगा.

Node.js डेमो का स्क्रीनशॉट

नोड आसान है. हमें Socket.io के कॉम्बिनेशन और पसंद के मुताबिक बनाए गए पोस्ट अनुरोधों का इस्तेमाल करके, सिंक करने के लिए मुश्किल रूटीन बनाने की ज़रूरत नहीं पड़ी. Socket.io इसे पारदर्शी तरीके से हैंडल करता है; JSON पास हो जाता है.

कितना आसान? इसे देखें.

JavaScript की तीन लाइनों वाला वेब सर्वर एक्सप्रेस के साथ चल रहा है.

//Tell  our Javascript file we want to use express.
var express = require('express');

//Create our web-server
var server = express.createServer();

//Tell express where to look for our static files.
server.use(express.static(__dirname + '/static/'));

रीयल-टाइम में बातचीत के लिए, network.io को और भी बेहतर बनाया जा सकता है.

var io = require('socket.io').listen(server);
//Start listening for socket commands
io.sockets.on('connection', function (socket) {
    //User is connected, start listening for commands.
    socket.on('someEventFromClient', handleEvent);

});

अब हमने एचटीएमएल पेज से आने वाले कनेक्शन का पता लगाना शुरू किया.

<!-- Socket-io will serve it-self when requested from this url. -->
<script type="text/javascript" src="/socket.io/socket.io.js"></script>

 <!-- Create our socket and connect to the server -->
 var sock = io.connect('http://localhost:8888');
 sock.on("connect", handleConnect);

 function handleConnect() {
    //Send a event to the server.
    sock.emit('someEventFromClient', 'someData');
 }
 ```

## Sound check

A big unknown was the effort entailed with using the Web Audio API. Our initial findings confirmed that [Digital Signal Processing](http://en.wikipedia.org/wiki/Digital_Signal_Processing) (DSP) is very complex, and we were likely in way over our heads. Second realization: [Chris Rogers](http://chromium.googlecode.com/svn/trunk/samples/audio/index.html) has already done the heavy lifting in the API.
Technitone isn't using any really complex math or audioholicism; this functionality is easily accessible to interested developers. We really just needed to brush up on some terminology and [read the docs](https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html). Our advice? Don't skim them. Read them. Start at the top and end at the bottom. They are peppered with diagrams and photos, and it's really cool stuff.

If this is the first you've heard of the Web Audio API, or don't know what it can do, hit up Chris Rogers' [demos](http://chromium.googlecode.com/svn/trunk/samples/audio/index.html). Looking for inspiration? You'll definitely find it there.

### Web Audio API Demo

Load in a sample (sound file)…

```js
/**
 * The XMLHttpRequest allows you to get the load
 * progress of your file download and has a responseType
 * of "arraybuffer" that the Web Audio API uses to
 * create its own AudioBufferNode.
 * Note: the 'true' parameter of request.open makes the
 * request asynchronous - this is required!
 */
var request = new XMLHttpRequest();
request.open("GET", "mySample.mp3", true);
request.responseType = "arraybuffer";
request.onprogress = onRequestProgress; // Progress callback.
request.onload = onRequestLoad; // Complete callback.
request.onerror = onRequestError; // Error callback.
request.onabort = onRequestError; // Abort callback.
request.send();

// Use this context to create nodes, route everything together, etc.
var context = new webkitAudioContext();

// Feed this AudioBuffer into your AudioBufferSourceNode:
var audioBuffer = null;

function onRequestProgress (event) {
    var progress = event.loaded / event.total;
}

function onRequestLoad (event) {
    // The 'true' parameter specifies if you want to mix the sample to mono.
    audioBuffer = context.createBuffer(request.response, true);
}

function onRequestError (event) {
    // An error occurred when trying to load the sound file.
}

...मॉड्युलर रूटिंग सेट अप करें...

/**
 * Generally you'll want to set up your routing like this:
 * AudioBufferSourceNode > [effect nodes] > CompressorNode > AudioContext.destination
 * Note: nodes are designed to be able to connect to multiple nodes.
 */

// The DynamicsCompressorNode makes the loud parts
// of the sound quieter and quiet parts louder.
var compressorNode = context.createDynamicsCompressor();
compressorNode.connect(context.destination);

// [other effect nodes]

// Create and route the AudioBufferSourceNode when you want to play the sample.

...कोई रनटाइम इफ़ेक्ट लागू करें (इंपल्स रिस्पॉन्स का इस्तेमाल करके कन्वर्ज़न)...

/**
 * Your routing now looks like this:
 * AudioBufferSourceNode > ConvolverNode > CompressorNode > AudioContext.destination
 */

var convolverNode = context.createConvolver();
convolverNode.connect(compressorNode);
convolverNode.buffer = impulseResponseAudioBuffer;

...कोई अन्य रनटाइम इफ़ेक्ट लागू करें (देरी)...

/**
 * The delay effect needs some special routing.
 * Unlike most effects, this one takes the sound data out
 * of the flow, reinserts it after a specified time (while
 * looping it back into itself for another iteration).
 * You should add an AudioGainNode to quieten the
 * delayed sound...just so things don't get crazy :)
 *
 * Your routing now looks like this:
 * AudioBufferSourceNode -> ConvolverNode > CompressorNode > AudioContext.destination
 *                       |  ^
 *                       |  |___________________________
 *                       |  v                          |
 *                       -> DelayNode > AudioGainNode _|
 */

var delayGainNode = context.createGainNode();
delayGainNode.gain.value = 0.7; // Quieten the feedback a bit.
delayGainNode.connect(convolverNode);

var delayNode = context.createDelayNode();
delayNode.delayTime = 0.5; // Re-sound every 0.5 seconds.
delayNode.connect(delayGainNode);

delayGainNode.connect(delayNode); // make the loop

...और फिर उसे सुनाना.

/**
 * Once your routing is set up properly, playing a sound
 * is easy-shmeezy. All you need to do is create an
 * AudioSourceBufferNode, route it, and tell it what time
 * (in seconds relative to the currentTime attribute of
 * the AudioContext) it needs to play the sound.
 *
 * 0 == now!
 * 1 == one second from now.
 * etc...
 */

var sourceNode = context.createBufferSource();
sourceNode.connect(convolverNode);
sourceNode.connect(delayNode);
sourceNode.buffer = audioBuffer;
sourceNode.noteOn(0); // play now!

Technitone में, वीडियो चलाने का हमारा मकसद सिर्फ़ शेड्यूल बनाना है. हर बीट की आवाज़ को प्रोसेस करने के लिए, अपनी टेंपो के बराबर टाइमर इंटरवल सेट करने के बजाय, हम एक छोटा इंटरवल सेट करते हैं. इससे सूची में मौजूद आवाज़ों को मैनेज और शेड्यूल किया जा सकता है. इससे एपीआई को ऑडियो डेटा की समस्या को हल करने और फ़िल्टर और इफ़ेक्ट को प्रोसेस करने में सबसे पहले मदद मिलती है. इसके बाद, हम सीपीयू को ऑडियो सुनने का टास्क देते हैं. जब आखिर में वह बीट आ जाती है, तो उसके पास स्पीकर के सामने कुल नतीजा पेश करने के लिए ज़रूरी सारी जानकारी पहले से मौजूद होती है.

कुल मिलाकर, हर चीज़ को ऑप्टिमाइज़ करने की ज़रूरत थी. जब हमने अपने सीपीयू बहुत मुश्किल बना दिए, तो शेड्यूल के हिसाब से प्रोसेस को (पॉप, क्लिक, स्क्रैच) नहीं किया गया. अगर आप Chrome के किसी दूसरे टैब पर जाते हैं, तो हमने इस सभी पागलपन को रोकने के लिए कड़ी मेहनत की.

लाइट शो

सामने और बीच का हिस्सा, ग्रिड और पार्टिकल टनल है. यह Technitone की WebGL लेयर है.

वेब पर विज़ुअल रेंडर करने के दूसरे ज़्यादातर तरीकों की तुलना में, WebGL बहुत बेहतर परफ़ॉर्म करता है. इसके लिए, प्रोसेसर के साथ मिलकर काम करने के लिए जीपीयू का इस्तेमाल किया जाता है. परफ़ॉर्मेंस में इस सुधार की वजह ज़्यादा डेवलपमेंट की लागत है, साथ ही इसमें बहुत ज़्यादा सीखने की प्रक्रिया भी शामिल है. हालांकि, अगर आपको वेब पर इंटरैक्टिव तरीके से इंटरैक्ट करना पसंद है और आपको परफ़ॉर्मेंस पर कम से कम पाबंदियां लगानी हैं, तो WebGL एक ऐसा समाधान उपलब्ध कराता है जो फ़्लैश से मिलता-जुलता है.

WebGL डेमो

WebGL सामग्री को कैनवस (वास्तव में, HTML5 कैनवस) पर रेंडर किया जाता है और इसमें ये मुख्य बिल्डिंग ब्लॉक होते हैं:

  • ऑब्जेक्ट वर्टेक्स (जियोमेट्री)
  • पोज़िशन के मैट्रिक्स (3D कोऑर्डिनेट)
    • शेडर (ज्यामिति के दिखने की जानकारी, जो सीधे जीपीयू से लिंक की जाती है)
    • संदर्भ ("उन एलिमेंट के शॉर्टकट" जिनके लिए जीपीयू का इस्तेमाल किया जाता है)
    • बफ़र (जीपीयू को कॉन्टेक्स्ट डेटा पास करने के लिए पाइपलाइन)
    • मुख्य कोड (पसंदीदा इंटरैक्टिव के लिए खास कारोबारी नियम)
    • "ड्रॉ" करने का तरीका (शेडर को चालू करता है और कैनवस पर पिक्सल बनाता है)

WebGL सामग्री को स्क्रीन पर रेंडर करने की बुनियादी प्रक्रिया इस तरह दिखती है:

  1. पर्सपेक्टिव मैट्रिक्स सेट करें (पिक्चर प्लेन को परिभाषित करते हुए, 3D स्पेस में पीयर करने वाले कैमरे की सेटिंग अडजस्ट करता है).
  2. पोज़िशन का मैट्रिक्स सेट करें (3D निर्देशांक में किसी ऐसे ऑरिजिन के बारे में बताएं जिसके हिसाब से पोज़िशन को मापा जाता है).
  3. शेडर के ज़रिए कॉन्टेक्स्ट तक पहुंचाने के लिए, बफ़र में डेटा (वर्टेक्स पोज़िशन, कलर, टेक्सचर्स...) डालें.
  4. शेडर की मदद से, बफ़र से डेटा निकालें और व्यवस्थित करें और उसे जीपीयू में पास करें.
  5. शेडर चालू करने, डेटा के साथ चलाने, और कैनवस अपडेट करने के लिए, कॉन्टेक्स्ट के बारे में बताने के लिए, ड्रॉ करने के तरीके को कॉल करें.

ऐसा लगता है कि यह काम करता है:

पर्सपेक्टिव मैट्रिक्स सेट करें...

// Aspect ratio (usually based off the viewport,
// as it can differ from the canvas dimensions).
var aspectRatio = canvas.width / canvas.height;

// Set up the camera view with this matrix.
mat4.perspective(45, aspectRatio, 0.1, 1000.0, pMatrix);

// Adds the camera to the shader. [context = canvas.context]
// This will give it a point to start rendering from.
context.uniformMatrix4fv(shader.pMatrixUniform, 0, pMatrix);

...रैंक का मैट्रिक्स सेट करें...

// This resets the mvMatrix. This will create the origin in world space.
mat4.identity(mvMatrix);

// The mvMatrix will be moved 20 units away from the camera (z-axis).
mat4.translate(mvMatrix, [0,0,-20]);

// Sets the mvMatrix in the shader like we did with the camera matrix.
context.uniformMatrix4fv(shader.mvMatrixUniform, 0, mvMatrix);

...कुछ ज्यामिति और लुक के बारे में बताएं...

// Creates a square with a gradient going from top to bottom.
// The first 3 values are the XYZ position; the last 4 are RGBA.
this.vertices = new Float32Array(28);
this.vertices.set([-2,-2, 0,    0.0, 0.0, 0.7, 1.0,
                   -2, 2, 0,    0.0, 0.4, 0.9, 1.0,
                    2, 2, 0,    0.0, 0.4, 0.9, 1.0,
                    2,-2, 0,    0.0, 0.0, 0.7, 1.0
                  ]);

// Set the order of which the vertices are drawn. Repeating values allows you
// to draw to the same vertex again, saving buffer space and connecting shapes.
this.indices = new Uint16Array(6);
this.indices.set([0,1,2, 0,2,3]);

...बफ़र में डेटा भरें और उसे कॉन्टेक्स्ट में भेजें...

// Create a new storage space for the buffer and assign the data in.
context.bindBuffer(context.ARRAY_BUFFER, context.createBuffer());
context.bufferData(context.ARRAY_BUFFER, this.vertices, context.STATIC_DRAW);

// Separate the buffer data into its respective attributes per vertex.
context.vertexAttribPointer(shader.vertexPositionAttribute,3,context.FLOAT,0,28,0);
context.vertexAttribPointer(shader.vertexColorAttribute,4,context.FLOAT,0,28,12);

// Create element array buffer for the index order.
context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, context.createBuffer());
context.bufferData(context.ELEMENT_ARRAY_BUFFER, this.indices, context.STATIC_DRAW);

...और ड्रॉ विधि को कॉल करें

// Draw the triangles based off the order: [0,1,2, 0,2,3].
// Draws two triangles with two shared points (a square).
context.drawElements(context.TRIANGLES, 6, context.UNSIGNED_SHORT, 0);

अगर आपको ऐल्फ़ा-आधारित विज़ुअल को एक-दूसरे पर स्टैक नहीं करना है, तो हर फ़्रेम को साफ़ करना न भूलें.

द वेन्यू

ग्रिड और पार्टिकल टनल के अलावा, बाकी सभी यूज़र इंटरफ़ेस (यूआई) एलिमेंट को एचटीएमएल / सीएसएस में और JavaScript में इंटरैक्टिव लॉजिक बनाया गया था.

शुरुआत से ही हमने यह फ़ैसला लिया कि उपयोगकर्ताओं को ग्रिड से जल्द से जल्द इंटरैक्ट करना चाहिए. न कोई स्प्लैश स्क्रीन, न कोई निर्देश, न कोई ट्यूटोरियल, बस 'शुरू करें'. अगर इंटरफ़ेस लोड हो गया है, तो उसकी रफ़्तार धीमी नहीं होनी चाहिए.

इसके लिए हमें इस बात पर ध्यान देना ज़रूरी था कि पहली बार आने वाले उपयोगकर्ता के इंटरैक्शन के ज़रिए कैसे उन्हें गाइड किया जाए. हमने आसान संकेतों को शामिल किया है. जैसे, WebGL स्पेस में उपयोगकर्ता के माउस की जगह के आधार पर सीएसएस कर्सर प्रॉपर्टी में बदलाव होना. अगर कर्सर ग्रिड के ऊपर है, तो इसे हैंड कर्सर पर स्विच किया जाता है. ऐसा इसलिए, क्योंकि वे टोन प्लॉट करके इंटरैक्ट कर सकते हैं. अगर इसे ग्रिड के आस-पास की खाली सफ़ेद जगह पर घुमाया जाता है, तो हम इसे दिशा वाले क्रॉस कर्सर की जगह इस्तेमाल करते हैं. इससे यह पता चलता है कि वे ग्रिड को घुमा सकते हैं या इसे लेयर में विस्फोट कर सकते हैं.

शो के लिए तैयार हो रहे हैं

लेस (सीएसएस प्री-प्रोसेसर) और CodeKit (स्टेरॉयड पर वेब डेवलपमेंट) ने डिज़ाइन फ़ाइलों को आसानी से एचटीएमएल/सीएसएस के तौर पर बदलने में लगने वाला समय कम किया. इनसे हमें सीएसएस को कई तरह से व्यवस्थित करने, लिखने, और उसे ऑप्टिमाइज़ करने में मदद मिलती है — वैरिएबल, मिक्स-इन (फ़ंक्शन), और यहां तक कि गणित का भी फ़ायदा मिलता है!

स्टेज इफ़ेक्ट

हमने CSS3 ट्रांज़िशन और backbone.js का इस्तेमाल करके, हमने कुछ बहुत आसान इफ़ेक्ट बनाए हैं. इनकी मदद से, ऐप्लिकेशन को हकीकत में बदला जा सकता है और उपयोगकर्ताओं को ऐसी विज़ुअल सूची मिलती है जिससे पता चलता है कि वे किस डिवाइस का इस्तेमाल कर रहे हैं.

टेक्नीटोन के रंग.

बैकबोन.js की मदद से, हम रंग में होने वाले बदलाव के इवेंट देख सकते हैं और सही डीओएम एलिमेंट पर नया रंग लागू कर सकते हैं. जीपीयू की रफ़्तार से चलने वाले CSS3 ट्रांज़िशन ने, परफ़ॉर्मेंस पर थोड़ा सा असर डालते हुए, कलर स्टाइल में होने वाले बदलावों को हैंडल किया.

इंटरफ़ेस एलिमेंट पर ज़्यादातर कलर ट्रांज़िशन, बैकग्राउंड के कलर में बदलाव करके बनाए जाते हैं. इस बैकग्राउंड के रंग में सबसे ऊपर, हम पारदर्शिता के अहम क्षेत्रों वाली बैकग्राउंड की इमेज का इस्तेमाल करते हैं. इससे बैकग्राउंड का रंग साफ़ दिखता है.

HTML: फ़ाउंडेशन

डेमो के लिए हमें तीन कलर रीजन की ज़रूरत थी: दो उपयोगकर्ताओं ने कलर रीजन और तीसरा मिक्स कलर रीजन. हमने सबसे आसान डीओएम स्ट्रक्चर बनाया है, जिसके बारे में हमें लगता है कि यह CSS3 ट्रांज़िशन के साथ काम करता है. साथ ही, हमने अपने इलस्ट्रेशन के लिए कम से कम एचटीटीपी अनुरोधों का इस्तेमाल किया है.

<!-- Basic HTML Setup -->
<div class="illo color-mixed">
  <div class="illo color-primary"></div>
  <div class="illo color-secondary"></div>
</div>

सीएसएस: स्टाइल के साथ आसान स्ट्रक्चर

हमने हर इलाके को उसकी सही जगह पर रखने के लिए, ऐब्सलूट पोज़िशनिंग का इस्तेमाल किया है. साथ ही, हर इलाके में बैकग्राउंड की तस्वीर को अलाइन करने के लिए, उसकी जगह की जानकारी में बदलाव किया है. इससे सभी इलाके (हर एक की बैकग्राउंड इमेज एक ही होती है) एक एलिमेंट की तरह लगते हैं.

.illo {
  background: url('../img/illo.png') no-repeat;
  top:        0;
  cursor:     pointer;
}
  .illo.color-primary, .illo.color-secondary {
    position: absolute;
    height:   100%;
  }
  .illo.color-primary {
    width:                350px;
    left:                 0;
    background-position:  top left;
  }
  .illo.color-secondary {
    width:                355px;
    right:                0;
    background-position:  top right;
  }

जीपीयू ऐक्सेलरेटेड ट्रांज़िशन लागू किए गए थे, जो रंग में बदलाव के इवेंट देखते हैं. हमने अवधि बढ़ा दी है और .color-mix को मिलाकर बनाए गए ईज़िंग में बदलाव किया है, ताकि यह इंप्रेशन मिले कि रंगों को मिक्स करने में समय लगा.

/* Apply Transitions To Backgrounds */
.color-primary, .color-secondary {
  -webkit-transition: background .5s linear;
  -moz-transition:    background .5s linear;
  -ms-transition:     background .5s linear;
  -o-transition:      background .5s linear;
}

.color-mixed {
  position:           relative;
  width:              750px;
  height:             600px;
  -webkit-transition: background 1.5s cubic-bezier(.78,0,.53,1);
  -moz-transition:    background 1.5s cubic-bezier(.78,0,.53,1);
  -ms-transition:     background 1.5s cubic-bezier(.78,0,.53,1);
  -o-transition:      background 1.5s cubic-bezier(.78,0,.53,1);
}

ब्राउज़र से जुड़ी मौजूदा सहायता और CSS3 ट्रांज़िशन के लिए सुझाए गए इस्तेमाल के बारे में जानने के लिए, कृपया HTML5 पर जाएं.

JavaScript: इससे काम हो रहा है

डाइनैमिक तौर पर कलर असाइन करना आसान है. हम अपनी कलर क्लास वाले किसी भी एलिमेंट के लिए डीओएम की खोज करते हैं. साथ ही, उपयोगकर्ता की पसंद के हिसाब से बैकग्राउंड का रंग सेट करते हैं. हम एक क्लास जोड़कर, डीओएम में मौजूद किसी भी एलिमेंट पर अपने ट्रांज़िशन इफ़ेक्ट को लागू करते हैं. इससे ऐसा आर्किटेक्चर बनता है जो लाइटवेट, लचीला, और बढ़ाया जा सकने वाला है.

function createPotion() {

    var primaryColor = $('.picker.color-primary > li.selected').css('background-color');
    var secondaryColor = $('.picker.color-secondary > li.selected').css('background-color');
    console.log(primaryColor, secondaryColor);
    $('.illo.color-primary').css('background-color', primaryColor);
    $('.illo.color-secondary').css('background-color', secondaryColor);

    var mixedColor = mixColors (
            parseColor(primaryColor),
            parseColor(secondaryColor)
    );

    $('.color-mixed').css('background-color', mixedColor);
}

प्राइमरी और सेकंडरी कलर चुनने के बाद, हम उनके मिले-जुले रंग की वैल्यू का हिसाब लगाते हैं. साथ ही, उससे मिलने वाली वैल्यू को सही डीओएम एलिमेंट को असाइन करते हैं.

// take our rgb(x,x,x) value and return an array of numeric values
function parseColor(value) {
    return (
            (value = value.match(/(\d+),\s*(\d+),\s*(\d+)/)))
            ? [value[1], value[2], value[3]]
            : [0,0,0];
}

// blend two rgb arrays into a single value
function mixColors(primary, secondary) {

    var r = Math.round( (primary[0] * .5) + (secondary[0] * .5) );
    var g = Math.round( (primary[1] * .5) + (secondary[1] * .5) );
    var b = Math.round( (primary[2] * .5) + (secondary[2] * .5) );

    return 'rgb('+r+', '+g+', '+b+')';
}

एचटीएमएल/सीएसएस आर्किटेक्चर का इलस्ट्रेशन: तीन कलर शिफ़्टिंग बॉक्स पर्सनैलिटी गिविंग

हमारा लक्ष्य एक मज़ेदार और असली लाइटिंग इफ़ेक्ट बनाना था, जो कंट्रास्ट रंगों को पास-पास मौजूद कलर रीजन में लगाने पर, बेहतरीन लाइटिंग इफ़ेक्ट बनाए रखता है.

24-बिट PNG फ़ॉर्मैट वाली इमेज, हमारे एचटीएमएल एलिमेंट के बैकग्राउंड कलर को इमेज की पारदर्शी जगहों से दिखाती है.

इमेज ट्रांसपैरेंस

रंग-बिरंगे बॉक्स, कठोर किनारे बनाते हैं जहां अलग-अलग रंग मिलते हैं. यह बिलकुल असली लाइटिंग इफ़ेक्ट के तौर पर काम करता था और यह इलस्ट्रेशन डिज़ाइन करते समय सबसे बड़ी चुनौतियों में से एक था.

रंग क्षेत्र

समस्या का हल यह था कि इलस्ट्रेशन को डिज़ाइन किया जाए, ताकि रंग वाले इलाकों के किनारों को पारदर्शी जगहों के ज़रिए कभी न दिखाया जाए.

रंग क्षेत्र के किनारे

बिल्ड की प्लानिंग बहुत ज़रूरी थी. डिज़ाइनर, डेवलपर, और इलस्ट्रेटर के बीच एक छोटे से प्लानिंग सेशन ने टीम को यह समझने में मदद की कि किस तरह की चीज़ों को बनाने की ज़रूरत है, ताकि असेंबल किए जाने पर वह सब एक साथ काम कर सके.

लेयर का नाम, सीएसएस बनाने के बारे में जानकारी कैसे दे सकता है, इसके उदाहरण के तौर पर Photoshop फ़ाइल देखें.

रंग क्षेत्र के किनारे

एनकोर

Chrome का इस्तेमाल नहीं करने वाले उपयोगकर्ताओं के लिए, हम ऐप्लिकेशन के इस्तेमाल को एक ही स्टैटिक इमेज में बदलने का लक्ष्य तय करते हैं. ग्रिड नोड हीरो बन गया, बैकग्राउंड की टाइल, ऐप्लिकेशन के मकसद को दिखाती हैं, और रिफ़्लेक्शन में मौजूद व्यू, ग्रिड के इमर्सिव 3D एनवायरमेंट की ओर इशारा करता है.

रंग क्षेत्र के किनारे.

अगर आपको Technitone के बारे में ज़्यादा जानना है, तो हमारा ब्लॉग देखते रहें.

बैंड

पढ़ने के लिए धन्यवाद. शायद हम जल्द ही आपके साथ बातचीत करने वाले हैं!