مطالعه موردی - ساختمان Technitone.com

شان میدلدیچ
Sean Middleditch
Technitone - یک تجربه صوتی وب.

Technitone.com تلفیقی از WebGL، Canvas، Web Sockets، CSS3، Javascript، Flash و Web Audio API جدید در کروم است.

این مقاله به هر جنبه ای از تولید اشاره می کند: پلان، سرور، صداها، تصاویر بصری و برخی از جریان کاری که برای طراحی تعاملی استفاده می کنیم. اکثر بخش ها شامل قطعه کد، نسخه نمایشی و دانلود هستند. در پایان مقاله، لینک دانلودی وجود دارد که می توانید همه آنها را به صورت یک فایل فشرده دریافت کنید.

تیم تولید gskinner.com.

کنسرت

ما در gskinner.com به هیچ وجه مهندس صدا نیستیم - اما ما را با یک چالش وسوسه کنید و یک برنامه را کشف خواهیم کرد:

  • کاربران صداها را بر روی یک شبکه "الهام گرفته از ToneMatrix " آندره ترسیم می کنند
  • زنگ‌ها به سازهای نمونه‌برداری شده، کیت‌های درام یا حتی ضبط‌های خود کاربر متصل می‌شوند
  • چندین کاربر متصل به طور همزمان در یک شبکه بازی می کنند
  • ... یا به حالت انفرادی بروید تا به تنهایی کاوش کنند
  • جلسات دعوت به کاربران این امکان را می دهد که یک گروه موسیقی را سازماندهی کنند و یک مرم بداهه داشته باشند

ما به کاربران فرصتی برای کاوش Web Audio API با استفاده از پانل ابزاری ارائه می‌دهیم که فیلترهای صوتی و افکت‌ها را روی آهنگ‌های آن‌ها اعمال می‌کند.

تکنیتون توسط gskinner.com

ما همچنین:

  • ترکیبات و جلوه‌های کاربران را به‌عنوان داده ذخیره کنید و آن‌ها را بین مشتریان همگام کنید
  • برخی از گزینه های رنگی را ارائه دهید تا بتوانند آهنگ های جذابی را ترسیم کنند
  • یک گالری ارائه دهید تا مردم بتوانند کارهای دیگران را بشنوند، دوست داشته باشند یا حتی ویرایش کنند

ما به استعاره شبکه آشنا چسبیدیم، آن را در فضای سه بعدی شناور کردیم، مقداری نور، بافت و جلوه های ذرات اضافه کردیم، آن را در یک رابط انعطاف پذیر (یا تمام صفحه) CSS و JS-driven قرار دادیم.

سفر جاده ای

داده‌های ابزار، افکت و شبکه روی کلاینت ادغام و سریال می‌شوند، سپس به باطن سفارشی Node.js فرستاده می‌شوند تا برای چندین کاربر à la Socket.io حل شود. این داده‌ها قبل از پخش شدن در لایه‌های CSS، WebGL و WebAudio که مسئول رندر کردن رابط کاربری، نمونه‌ها و افکت‌ها در حین پخش چند کاربره هستند، با مشارکت هر بازیکن به مشتری ارسال می‌شود.

ارتباط بلادرنگ با سوکت ها جاوا اسکریپت را روی کلاینت و جاوا اسکریپت را روی سرور تغذیه می کند.

نمودار سرور تکنیتون

ما از Node برای هر جنبه ای از سرور استفاده می کنیم. این یک وب سرور ثابت و سرور سوکت ما همه در یک است. Express همان چیزی است که ما در نهایت از آن استفاده کردیم، این یک وب سرور کامل است که به طور کامل بر روی Node ساخته شده است. این فوق العاده مقیاس پذیر است، بسیار قابل تنظیم است، و جنبه های سرور سطح پایین را برای شما مدیریت می کند (درست مانند آپاچی یا ویندوز سرور). سپس شما به عنوان توسعه دهنده، تنها باید روی ساخت اپلیکیشن خود تمرکز کنید.

نسخه ی نمایشی چند کاربره (خوب، در واقع فقط یک اسکرین شات است)

این نسخه آزمایشی باید از سرور Node اجرا شود، و از آنجایی که این مقاله یکی نیست، ما یک اسکرین شات از ظاهر نسخه نمایشی پس از نصب Node.js، پیکربندی وب سرور خود و اجرای آن به صورت محلی اضافه کرده ایم. . هر بار که کاربر جدیدی از نصب دمو شما بازدید می‌کند، یک شبکه جدید اضافه می‌شود و کار همه برای یکدیگر قابل مشاهده است.

اسکرین شات از Node.js Demo

گره آسان است. با استفاده از ترکیبی از Socket.io و درخواست های سفارشی POST، نیازی به ساخت روال های پیچیده برای همگام سازی نداشتیم. Socket.io به طور شفاف این کار را انجام می دهد. JSON به اطراف منتقل می شود.

چقدر راحت این را نگاه کن.

با 3 خط جاوا اسکریپت، ما یک وب سرور داریم که با Express کار می کند.

//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/'));

چند مورد دیگر برای اتصال socket.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);

});

اکنون ما فقط شروع به گوش دادن به اتصالات ورودی از صفحه HTML می کنیم.

<!-- 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 همه چیز در مورد زمان بندی است. به جای تنظیم فاصله زمانی برابر با سرعت خود برای پردازش صداها در هر ضرب، فاصله کمتری را تنظیم می کنیم که صداها را در یک صف مدیریت و زمان بندی می کند. این به API اجازه می‌دهد تا قبل از اینکه CPU را مجبور به شنیدن کردن آن کنیم، کار مقدماتی حل کردن داده‌های صوتی و پردازش فیلترها و جلوه‌ها را انجام دهد. هنگامی که این ضربان در نهایت آمد، از قبل تمام اطلاعات مورد نیاز برای ارائه نتیجه خالص به سخنرانان را دارد.

به طور کلی، همه چیز باید بهینه می شد. هنگامی که ما CPU های خود را خیلی سخت فشار دادیم، فرآیندها (پاپ، کلیک، اسکرچ) به منظور همگام شدن با برنامه زمانبندی حذف شدند. اگر به برگه دیگری در Chrome بروید، ما تلاش جدی برای متوقف کردن همه جنون انجام می دهیم.

نمایش نور

جلو و مرکز شبکه و تونل ذرات ما است. این لایه WebGL Technitone است.

WebGL نسبت به بسیاری از روش‌های دیگر برای رندر کردن تصاویر در وب، عملکرد بهتری را ارائه می‌کند، زیرا GPU را به کار در ارتباط با پردازنده می‌پردازد. این افزایش عملکرد با هزینه توسعه بسیار بیشتر همراه با منحنی یادگیری بسیار تندتر همراه است. گفته می‌شود، اگر واقعاً به تعامل در وب علاقه دارید و می‌خواهید تا حد ممکن محدودیت‌های عملکردی کمتری داشته باشید، WebGL راه‌حلی قابل مقایسه با Flash ارائه می‌دهد.

نسخه ی نمایشی WebGL

محتوای WebGL به یک بوم (به معنای واقعی کلمه بوم HTML5) ارائه می شود و از این بلوک های اصلی تشکیل شده است:

  • رئوس شی (هندسه)
  • ماتریس های موقعیت (مختصات سه بعدی)
    • سایه بان ها (توضیحات ظاهری هندسی که مستقیماً به GPU پیوند داده شده است)
    • زمینه ("میانبرهایی" برای عناصری که GPU به آنها اشاره می کند)
    • بافرها (خطوط برای انتقال داده های زمینه به GPU)
    • کد اصلی (منطق تجاری خاص برای تعاملی مورد نظر)
    • روش "draw" (شایدرها را فعال می کند و پیکسل ها را روی بوم می کشد)

فرآیند اصلی رندر کردن محتوای WebGL روی صفحه به این صورت است:

  1. ماتریس پرسپکتیو را تنظیم کنید (تنظیمات دوربینی را که به فضای سه بعدی نگاه می کند و صفحه تصویر را مشخص می کند) تنظیم می کند.
  2. ماتریس موقعیت را تنظیم کنید (منشا را در مختصات سه بعدی که موقعیت ها نسبت به آن اندازه گیری می شوند، اعلام کنید).
  3. بافرها را با داده ها پر کنید (موقعیت راس، رنگ، بافت ها...) تا از طریق سایه زن ها به متن منتقل شوند.
  4. استخراج و سازماندهی داده ها از بافرها با سایه بان ها و ارسال آن به GPU.
  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);

در هر فریم، اگر نمی‌خواهید تصاویر مبتنی بر آلفا روی یکدیگر قرار بگیرند، به یاد داشته باشید که بوم را پاک کنید.

محل برگزاری

علاوه بر شبکه و تونل ذرات، هر عنصر UI دیگر در HTML / CSS و منطق تعاملی در جاوا اسکریپت ساخته شده است.

از همان ابتدا، ما تصمیم گرفتیم که کاربران باید در سریع ترین زمان ممکن با شبکه تعامل داشته باشند. بدون صفحه نمایش، بدون دستورالعمل، بدون آموزش، فقط "برو". اگر اینترفیس بارگذاری شده باشد - نباید چیزی باعث کاهش سرعت آنها شود.

این امر مستلزم این بود که به دقت به نحوه راهنمایی یک کاربر برای اولین بار از طریق تعاملات خود نگاه کنیم. ما نشانه های ظریفی مانند تغییر ویژگی مکان نما CSS بر اساس موقعیت ماوس کاربر در فضای WebGL را اضافه کردیم. اگر مکان‌نما روی شبکه باشد، آن را به مکان‌نمای دستی تغییر می‌دهیم (زیرا آنها می‌توانند با ترسیم صداها تعامل داشته باشند). اگر در فضای سفید اطراف شبکه معلق باشد، آن را با مکان نما متقاطع جهت دار عوض می کنیم (برای اینکه نشان دهیم می توانند بچرخند یا شبکه را به لایه ها تبدیل کنند).

آماده شدن برای نمایش

LESS (یک پیش پردازشگر CSS) و CodeKit (توسعه وب روی استروئیدها) واقعاً زمان لازم برای ترجمه فایل‌های طراحی به HTML/CSS را کاهش می‌دهند. اینها به ما اجازه می‌دهند CSS را به شیوه‌ای بسیار متنوع‌تر سازماندهی، بنویسیم و بهینه کنیم - از متغیرها، ترکیب‌ها (توابع) و حتی ریاضی استفاده کنیم!

جلوه های صحنه

با استفاده از CSS3 transitions و backbone.js ما چند افکت واقعا ساده ایجاد کردیم که به زنده کردن برنامه کمک می‌کند و صف‌های بصری را در اختیار کاربران قرار می‌دهد که نشان می‌دهد از کدام ابزار استفاده می‌کنند.

رنگ های تکنیتون.

Backbone.js به ما اجازه می دهد تا رویدادهای تغییر رنگ را مشاهده کنیم و رنگ جدید را در عناصر DOM مناسب اعمال کنیم. انتقال‌های CSS3 تسریع‌شده توسط GPU، تغییرات سبک رنگ را با تأثیری کم یا بدون تأثیر بر عملکرد کنترل کردند.

بسیاری از انتقال رنگ در عناصر رابط توسط انتقال رنگ های پس زمینه ایجاد شده است. در بالای این رنگ پس‌زمینه، تصاویر پس‌زمینه را با مناطق استراتژیک شفاف قرار می‌دهیم تا رنگ پس‌زمینه بدرخشد.

HTML: بنیاد

ما به سه منطقه رنگی برای نسخه آزمایشی نیاز داشتیم: دو منطقه رنگی انتخاب شده توسط کاربر و یک منطقه رنگی ترکیبی سوم. ما ساده‌ترین ساختار DOM را ساختیم که می‌توانستیم فکر کنیم که از انتقال‌های CSS3 و کمترین درخواست‌های HTTP برای تصویر ما پشتیبانی می‌کند.

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

CSS: ساختار ساده با سبک

ما از موقعیت یابی مطلق برای قرار دادن هر منطقه در مکان صحیح خود استفاده کردیم و ویژگی پس زمینه موقعیت را برای تراز کردن تصویر پس زمینه در هر منطقه تنظیم کردیم. این باعث می‌شود که همه مناطق (هر کدام با یک تصویر پس‌زمینه) مانند یک عنصر به نظر برسند.

.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;
  }

انتقال‌های تسریع‌شده GPU اعمال شدند که به رویدادهای تغییر رنگ گوش می‌دهند. ما مدت زمان را افزایش دادیم و easing را در .color-mixed تغییر دادیم تا این تصور ایجاد شود که ترکیب رنگ ها زمان می برد.

/* 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 دیدن کنید.

جاوا اسکریپت: ساخت آن کار کند

تخصیص رنگ ها به صورت پویا ساده است. ما DOM را برای هر عنصر با کلاس رنگ خود جستجو می کنیم و رنگ پس زمینه را بر اساس انتخاب رنگ کاربر تنظیم می کنیم. با افزودن یک کلاس، افکت انتقال خود را به هر عنصری در DOM اعمال می کنیم. این یک معماری سبک وزن، انعطاف پذیر و مقیاس پذیر ایجاد می کند.

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);
}

هنگامی که رنگ های اصلی و ثانویه انتخاب شدند، مقدار رنگ مخلوط آنها را محاسبه می کنیم و مقدار حاصل را به عنصر DOM مناسب اختصاص می دهیم.

// 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+')';
}

تصویرسازی برای معماری HTML/CSS: شخصیت دادن به سه جعبه تغییر رنگ

هدف ما ایجاد یک جلوه نورپردازی سرگرم‌کننده و واقعی بود که یکپارچگی خود را در هنگام قرار دادن رنگ‌های متضاد در مناطق رنگی مجاور حفظ کند.

یک PNG 24 بیتی به رنگ پس‌زمینه عناصر HTML ما اجازه می‌دهد تا در قسمت‌های شفاف تصویر نشان داده شود.

شفافیت های تصویر

جعبه های رنگی لبه های سختی را در جایی که رنگ های مختلف به هم می رسند ایجاد می کنند. این مانع جلوه‌های نورپردازی واقعی می‌شود و یکی از چالش‌های بزرگ‌تر هنگام طراحی تصویر بود.

مناطق رنگی

راه حل این بود که تصویر را به گونه ای طراحی کنیم که هرگز اجازه ندهد لبه های مناطق رنگی از طریق مناطق شفاف نشان داده شوند.

لبه های منطقه رنگی

برنامه ریزی برای ساخت بسیار مهم بود. یک جلسه برنامه ریزی سریع بین طراح، توسعه دهنده و تصویرگر به تیم کمک کرد تا بفهمند که چگونه همه چیز باید ساخته شود تا هنگام مونتاژ با هم کار کنند.

فایل فتوشاپ را به عنوان مثالی بررسی کنید که چگونه نامگذاری لایه ها می تواند اطلاعاتی را در مورد ساخت و ساز CSS منتقل کند.

لبه های منطقه رنگی

Encore

برای کاربران بدون کروم، هدف ما این است که ماهیت برنامه را به یک تصویر ثابت تبدیل کنیم. گره گرید به قهرمان تبدیل شد، کاشی های پس زمینه به هدف برنامه اشاره می کنند، و چشم انداز موجود در بازتاب به محیط سه بعدی غوطه ور شبکه اشاره می کند.

لبه های منطقه رنگی.

اگر علاقه مند به کسب اطلاعات بیشتر در مورد Technitone هستید، با وبلاگ ما همراه باشید.

گروه

ممنون که خواندید، شاید به زودی با شما پارازیت کنیم!