apeescape2.com
  • メイン
  • デザイナーライフ
  • Uiデザイン
  • ヒントとツール
  • 収益と成長
Webフロントエンド

CSSとJavaScriptを使用してカスタムのフルページスライダーを作成する

私はカスタムのフルスクリーンレイアウトを実際に日常的に頻繁に使用しています。通常、これらのレイアウトは、かなりの量のインタラクションとアニメーションを意味します。トランジションのタイムトリガーされた複雑なタイムラインであろうと、スクロールベースのユーザー主導のイベントセットであろうと、ほとんどの場合、UIには、いくつかの調整と変更を加えた、すぐに使用できるプラグインソリューションを使用するだけでは不十分です。 。一方で、たくさん見かけます JavaScript開発者 タスクが特定のプラグインが提供するすべてのベルやホイッスルを必要としない場合でも、仕事を簡単にするためにお気に入りのJSプラグインに手を伸ばす傾向があります。

免責事項: もちろん、そこにある多くのプラグインの1つを使用すると、その特典があります。多くのコーディングを行うことなく、ニーズに合わせて調整するために使用できるさまざまなオプションを利用できます。また、ほとんどのプラグイン作成者は、コードを最適化し、クロスブラウザーおよびクロスプラットフォーム互換にするなどしています。しかし、それでも、プロジェクトに含まれるフルサイズのライブラリは、1つまたは2つの異なるものだけで提供されます。サードパーティのプラグインを使用することが当然悪いことだと言っているわけではありません。プロジェクトでは毎日使用しています。一般的に、各アプローチの長所と短所をそのまま比較検討することをお勧めします。コーディングの良い習慣。この方法で独自のことを行う場合、探しているものを知るには、コーディングの知識と経験が少し必要になりますが、最終的には、1つのことと1つのことだけを行うコードを取得する必要があります。あなたはそれをしたいです。

この記事は、カスタムコンテンツアニメーションを使用してフルスクリーンのスクロールトリガースライダーレイアウトを開発する際の純粋なCSS / JSアプローチを示すことを目的としています。この縮小されたアプローチでは、CMSバックエンドの最新のCSSから提供されると予想される基本的なHTML構造について説明します( SCSS )レイアウト手法、および完全な対話性のためのバニラJavaScriptコーディング。この概念は必要最低限​​のものであるため、大規模なプラグインに簡単に拡張したり、コアに依存関係のないさまざまなアプリケーションで使用したりできます。



私たちが作成しようとしているデザインは、各プロジェクトの注目の画像とタイトルを備えたミニマルな建築家ポートフォリオのショーケースです。アニメーション付きの完全なスライダーは次のようになります。

建築家ポートフォリオのサンプルスライダー。

あなたはデモをチェックすることができます ここに 、そしてあなたは私のにアクセスすることができます Githubリポジトリ 詳細については。

購入者に関する次の機能のうち、購入者が高い交渉力を持っていることを示しているのはどれですか?

HTMLの概要

使用する基本的なHTMLは次のとおりです。

hero-slider

IDが

#64 Paradigm

のdiv私たちのメインホルダーです。内部では、レイアウトはセクションに分割されています。

  • ロゴ(静的セクション)
  • 主に取り組むスライドショー
  • 情報(静的セクション)
  • 現在アクティブなスライドとスライドの総数を示すスライダーナビゲーション

スライドショーのセクションに焦点を当てましょう。それがこの記事の関心の対象です。ここには2つの部分があります— メイン そして に 。 Mainは注目の画像を含むdivで、auxは画像のタイトルを保持します。これら2つのホルダー内の各スライドの構造はかなり基本的です。メインホルダーの内側に画像スライドがあります。

#hero-slider { position: relative; height: 100vh; display: flex; background: $dark-color; } #slideshow { position: relative; flex: 1 1 $main-width; display: flex; align-items: flex-end; padding: $offset; } #info { position: relative; flex: 1 1 $side-width; padding: $offset; background-color: #fff; }

インデックスデータ属性は、スライドショーのどこにいるかを追跡するために使用するものです。興味深いトランジション効果を作成するために使用するabs-maskdivと、slide-imagedivには特定の注目画像が含まれています。画像は、CMSから直接取得されたかのようにインラインでレンダリングされ、エンドユーザーによって設定されます。

同様に、タイトルはAuxホルダーの内側にスライドします。

#slideshow { position: relative; flex: 1 1 $main-width; display: flex; align-items: flex-end; padding: $offset; } #slides-main { @extend %abs; &:after { content: ''; @extend %abs; background-color: rgba(0, 0, 0, .25); z-index: 100; } .slide-image { @extend %abs; background-position: center; background-size: cover; z-index: -1; } } #slides-aux { position: relative; top: 1.25rem; width: 100%; .slide-title { position: absolute; z-index: 300; font-size: 4vw; font-weight: 700; line-height: 1.3; @include outlined(#fff); } }

各スライドタイトルは、対応するデータ属性とそのプロジェクトの単一ページにつながることができるリンクを持つH2タグです。

HTMLの残りの部分も非常に単純です。上部にロゴ、ユーザーがどのページを表示しているかを示す静的情報、説明、スライダーの現在/合計インジケーターがあります。

CSSの概要

ソースCSSコードはで書かれています SCSS 、ブラウザが解釈できる通常のCSSにコンパイルされるCSSプリプロセッサ。 SCSSには、変数、ネストされた選択、ミックスイン、その他の優れた機能を使用できるという利点がありますが、ブラウザーにコードを正しく読み取らせるには、CSSにコンパイルする必要があります。このチュートリアルの目的のために、私は使用しました スカウト-アプリ 最小限のツールが必要だったので、コンパイルを処理します。

フレックスボックスを使用して、基本的なサイドバイサイドレイアウトを処理しました。アイデアは、片側にスライドショーを、反対側に情報セクションを配置することです。

background-size: cover

ポジショニングについて詳しく見ていきましょう。もう一度、スライドショーのセクションに注目してください。

%abs { position: absolute; top: 0; left: 0; height: 100%; width: 100%; }

@mixin outlined($color: $dark-color, $size: 1px) { color: transparent; -webkit-text-stroke: $size $color; } を使用して、メインスライダーを絶対位置に設定し、背景画像を領域全体に拡大しました。プロパティ。スライドのタイトルとのコントラストを高めるために、オーバーレイとして機能する絶対疑似要素を設定しました。スライドのタイトルを含む補助スライダーは、画面の下部と画像の上部に配置されています。

一度に表示されるスライドは1つだけなので、各タイトルも絶対値に設定し、カットオフがないことを確認するためにJSを介してホルダーサイズを計算しますが、それについては次のセクションの1つで詳しく説明します。ここでは、extendingと呼ばれるSCSS機能の使用法を見ることができます。

.slider-title-wrapper { position: absolute; top: $offset; left: calc(100% - #{$offset}); transform-origin: 0% 0%; transform: rotate(90deg); @include outlined; }

私は絶対測位を頻繁に使用したので、このCSSを拡張可能にプルして、さまざまなセレクターで簡単に使用できるようにしました。また、タイトルとメインスライダーのタイトルをスタイリングするときにDRYアプローチを提供するために、「アウトライン」と呼ばれるミックスインを作成しました。

transform-origin

このレイアウトの静的な部分に関しては、複雑なことは何もありませんが、ここでは、通常のフローではなくY軸上にある必要があるテキストを配置するときに興味深い方法を見ることができます。

#logo:after { transform: scaleY(0); transform-origin: 50% 0; transition: transform .35s $easing; } .logo-text { display: block; transform: translate3d(120%, 0, 0); opacity: 0; transition: transform .8s .2s, opacity .5s .2s; } .current, .sep:before { opacity: 0; transition: opacity .4s 1.3s; } #info { transform: translate3d(100%, 0, 0); transition: transform 1s $easing .6s; } .line { transform-origin: 0% 0; transform: scaleX(0); transition: transform .7s $easing 1s; } .slider-title { overflow: hidden; >span { display: block; transform: translate3d(0, -100%, 0); transition: transform .5s 1.5s; } }

transformに注目したいのですがこのタイプのレイアウトでは実際には十分に活用されていないことがわかったので、プロパティ。この要素の配置方法は、アンカーが要素の左上隅にとどまり、回転ポイントを設定し、さまざまな画面サイズに関して問題なく、テキストがそのポイントから下に向かって継続的に流れるようにすることです。

より興味深いCSSの部分である初期読み込みアニメーションを見てみましょう。

スライダーのアニメーションを読み込みます。

通常、この種の同期されたアニメーションの動作は、ライブラリを使用して実現されます- GSAP たとえば、は、優れたレンダリング機能を提供し、使いやすく、開発者がプロ​​グラムで要素の遷移を相互に連鎖できるようにするタイムライン機能を備えた、最高の製品の1つです。

ただし、これは純粋なCSS / JSの例であるため、ここでは本当に基本的なものにすることにしました。そのため、各要素はデフォルトで開始位置に設定されています。変換または不透明度によって非表示になり、JSによってトリガーされるスライダーの読み込み時に表示されます。すべてのトランジションプロパティは手動で調整され、自然で興味深いフローを確保します。各トランジションは別のトランジションに続き、快適な視覚体験を提供します。

transform

ここで見てほしいことが1つあるとすれば、それはheroSliderの使用です。プロパティ。 HTML要素を移動するときは、トランジションであろうとアニメーションであろうと、utilsを使用することをお勧めします。プロパティ。余白やパディング、さらにはオフセット(上、左など)を使用する傾向があり、レンダリングに関しては適切な結果が得られない人がたくさんいます。

インタラクティブな動作を追加するときにCSSを使用する方法をより深く理解するために、私はお勧めできませんでした 次の記事 足りる。

ChromeエンジニアのPaulLewisによるもので、CSSであろうとJSであろうと、ウェブでのピクセルレンダリングについて知っておくべきことのほとんどすべてをカバーしています。

JavaScriptの概要とスライダーロジック

JavaScriptファイルは2つの異なる機能に分かれています。

initここで必要なすべての機能を処理する関数、およびresize再利用可能なユーティリティ関数をいくつか追加した関数。 プロジェクトでそれらを再利用しようとしている場合にコンテキストを提供するために、これらのユーティリティ関数のそれぞれにコメントしました。

main関数は、次の2つのブランチを持つようにコーディングされています。initおよびheroSlider。これらのブランチは、main関数の戻りを介して利用可能であり、必要に応じて呼び出されます。 const slider = { hero: document.querySelector('#hero-slider'), main: document.querySelector('#slides-main'), aux: document.querySelector('#slides-aux'), current: document.querySelector('#slider-nav .current'), handle: null, idle: true, activeIndex: -1, interval: 3500 }; はメイン関数の初期化であり、ウィンドウの読み込みイベントでトリガーされます。同様に、サイズ変更ブランチはウィンドウのサイズ変更時にトリガーされます。サイズ変更機能の唯一の目的は、タイトルのフォントサイズが異なる可能性があるため、ウィンドウのサイズ変更時にタイトルのスライダーサイズを再計算することです。

handleで関数、必要なすべてのデータとセレクターを含むスライダーオブジェクトを提供しました。

idle

補足として、このアプローチは、たとえばReactを使用している場合、データを状態で保存したり、新しく追加されたフックを使用したりできるため、簡単に適応できます。要点を維持するために、ここでのKey-Valueペアのそれぞれが表すものを見てみましょう。

  • 最初の4つのプロパティは、操作するDOM要素へのHTML参照です。
  • activeIndexプロパティは、自動再生機能を開始および停止するために使用されます。
  • intervalプロパティは、スライドの移行中にユーザーが強制的にスクロールできないようにするフラグです。
  • setHeight(slider.aux, slider.aux.querySelectorAll('.slide-title')); loadingAnimation(); 現在アクティブなスライドを追跡できるようになります
  • setHeightスライダーの自動再生間隔を示します

スライダーの初期化時に、2つの関数を呼び出します。

処理で描画する方法
const loadingAnimation = function () { slider.hero.classList.add('ready'); slider.current.addEventListener('transitionend', start, { once: true }); }

const start = function () { autoplay(true); wheelControl(); window.innerWidth <= 1024 && touchControl(); slider.aux.addEventListener('transitionend', loaded, { once: true }); } 関数はユーティリティ関数にアクセスして、最大タイトルサイズに基づいて補助スライダーの高さを設定します。このようにして、適切なサイズが提供され、コンテンツが2行に落ちてもスライドタイトルが途切れないようにします。

LoadingAnimation関数は、CSSクラスを要素に追加してイントロCSSトランジションを提供します。

loadingAnimation

スライダーインジケーターはCSS遷移タイムラインの最後の要素であるため、遷移が終了するのを待って、開始関数を呼び出します。オブジェクトとして追加のパラメーターを提供することにより、これが1回だけトリガーされるようにします。

開始関数を見てみましょう。

const autoplay = function (initial) { slider.autoplay = true; slider.items = slider.hero.querySelectorAll('[data-index]'); slider.total = slider.items.length / 2; const loop = () => changeSlide('next'); initial && requestAnimationFrame(loop); slider.handle = utils().requestInterval(loop, slider.interval); }

したがって、レイアウトが終了すると、最初の遷移はslideChangeによってトリガーされます。関数と開始関数が引き継ぎます。次に、自動再生機能をトリガーし、ホイールコントロールを有効にし、タッチデバイスとデスクトップデバイスのどちらを使用しているかを判断し、タイトルスライドの最初の遷移を待って適切なCSSクラスを追加します。

自動再生

このレイアウトのコア機能の1つは、自動再生機能です。対応する関数を見てみましょう。

requestAnimationFrame

まず、自動再生フラグをtrueに設定して、スライダーが自動再生モードになっていることを示します。このフラグは、ユーザーがスライダーを操作した後に自動再生を再トリガーするかどうかを決定するときに役立ちます。次に、すべてのスライダーアイテム(スライド)を参照します。アクティブなクラスを変更し、2つの同期されたスライダーレイアウト(メインと補助)があるため、すべてのアイテムを合計して2で割ることにより、スライダーの合計反復回数を計算します。しかし、両方を同時に変更する「スライダー」自体は1つだけです。

ここでのコードの最も興味深い部分は、ループ関数です。 requestAnimationFrameを呼び出して、1分で通過するスライド方向を提供しますが、ループ関数は数回呼び出されます。理由を見てみましょう。

最初の引数がtrueと評価された場合、ループ関数を次のように呼び出します。 requestInterval 折り返し電話。これは、スライドの即時変更をトリガーする最初のスライダーのロード時にのみ発生します。 setIntervalを使用する次のフレームの再描画の直前に、提供されたコールバックを実行します。

デザインパターン定義の原則

スライダーの作成に使用される手順の図。

ただし、自動再生モードでスライドを読み続けたいので、この同じ関数を繰り返し呼び出します。これは通常、setIntervalで実現されます。ただし、この場合、ユーティリティ関数の1つであるrequestIntervalを使用します。 requestAnimationFrameうまくいくでしょう、slider.handle cancelAnimationFrameに依存する高度な概念ですよりパフォーマンスの高いアプローチを提供します。これにより、ブラウザのタブがアクティブな場合にのみ機能が再トリガーされます。

この素晴らしい記事のこの概念の詳細については、 CSSのトリック 。この関数からの戻り値をslideChangeに割り当てることに注意してください。プロパティ。関数が返すこの一意のIDは利用可能であり、後で使用して自動再生をキャンセルするために使用します const changeSlide = function (direction) { slider.idle = false; slider.hero.classList.remove('prev', 'next'); if (direction == 'next') { slider.activeIndex = (slider.activeIndex + 1) % slider.total; slider.hero.classList.add('next'); } else { slider.activeIndex = (slider.activeIndex - 1 + slider.total) % slider.total; slider.hero.classList.add('prev'); } //reset classes utils().removeClasses(slider.items, ['prev', 'active']); //set prev const prevItems = [...slider.items] .filter(item => { let prevIndex; if (slider.hero.classList.contains('prev')) { prevIndex = slider.activeIndex == slider.total - 1 ? 0 : slider.activeIndex + 1; } else { prevIndex = slider.activeIndex == 0 ? slider.total - 1 : slider.activeIndex - 1; } return item.dataset.index == prevIndex; }); //set active const activeItems = [...slider.items] .filter(item => { return item.dataset.index == slider.activeIndex; }); utils().addClasses(prevItems, ['prev']); utils().addClasses(activeItems, ['active']); setCurrent(); const activeImageItem = slider.main.querySelector('.active'); activeImageItem.addEventListener('transitionend', waitForIdle, { once: true }); } 。

スライドの変更

wheelControl関数は、概念全体の主要な関数です。自動再生によるか、ユーザートリガーによるかを問わず、スライドを変更します。スライダーの方向を認識し、ループを提供するため、最後のスライドに到達したときに最初のスライドに進むことができます。これが私がそれをコーディングした方法です:

touchControl

アイデアは、HTMLから取得したデータインデックスに基づいてアクティブなスライドを決定することです。各ステップについて説明しましょう。

  1. スライダーアイドルフラグをfalseに設定します。これは、スライドの変更が進行中であり、ホイールとタッチのジェスチャが無効になっていることを示しています。
  2. 以前のスライダー方向のCSSクラスがリセットされ、新しいクラスがチェックされます。方向パラメーターは、自動再生関数から取得する場合はデフォルトで「次へ」として提供されるか、ユーザーが呼び出す関数によって提供されます– setCurrentまたはwaitForIdle。
  3. 方向に基づいて、アクティブなスライドインデックスを計算し、現在の方向のCSSクラスをスライダーに提供します。このCSSクラスは、使用する遷移効果を決定するために使用されます(たとえば、右から左または左から右)
  4. スライドは、CSSクラスを削除するが、単一のDOM要素ではなく、NodeListで呼び出すことができる別のユーティリティ関数を使用して、「状態」のCSSクラス(前、アクティブ)をリセットします。その後、以前のスライドと現在アクティブなスライドのみがそれらのCSSクラスを追加します。これにより、CSSはそれらのスライドのみを対象とし、適切な遷移を提供できます。
  5. const wheelControl = function () { slider.hero.addEventListener('wheel', e => { if (slider.idle) { const direction = e.deltaY > 0 ? 'next' : 'prev'; stopAutoplay(); changeSlide(direction); } }); } activeIndexに基づいてスライダーインジケーターを更新するコールバックです。
  6. 最後に、stopAutoplayをトリガーするために、アクティブな画像スライドの遷移が終了するのを待ちます。以前にユーザーによって中断された場合に自動再生を再開するコールバック。

ユーザーコントロール

画面サイズに基づいて、ホイールとタッチの2種類のユーザーコントロールを追加しました。ホイールコントロール:

stopAutoplay

ここでは、ホイールをリッスンし、スライダーが現在アイドルモード(現在はスライドの変更をアニメーション化していない)の場合、ホイールの方向を決定し、cancelRequestIntervalを呼び出します。自動再生機能が進行中の場合は停止し、方向に基づいてスライドを変更します。 const stopAutoplay = function () { slider.autoplay = false; utils().clearRequestInterval(slider.handle); } 関数は、自動再生フラグをfalse値に設定し、wheelControlを呼び出すことによって間隔をキャンセルする単純な関数に他なりません。適切なハンドルを渡すユーティリティ関数:

touchControl

const touchControl = function () { const touchStart = function (e) { slider.ts = parseInt(e.changedTouches[0].clientX); window.scrollTop = 0; } const touchMove = function (e) { slider.tm = parseInt(e.changedTouches[0].clientX); const delta = slider.tm - slider.ts; window.scrollTop = 0; if (slider.idle) { const direction = delta <0 ? 'next' : 'prev'; stopAutoplay(); changeSlide(direction); } } slider.hero.addEventListener('touchstart', touchStart); slider.hero.addEventListener('touchmove', touchMove); } と同様に、touchstartがあります。タッチジェスチャを処理します。

touchmove

2つのイベントをリッスンします:slideChangeおよびslideChange。次に、差を計算します。負の値が返された場合、ユーザーが右から左にスワイプすると、次のスライドに移動します。一方、値が正の場合、つまりユーザーが左から右にスワイプした場合は、divをトリガーします。 「前」として渡された方向で。どちらの場合も、自動再生機能は停止します。

これは非常に単純なユーザージェスチャーの実装です。これに基づいて、前/次のボタンを追加してabs-maskをトリガーできます。クリックするか、箇条書きを追加して、インデックスに基づいてスライドに直接移動します。

CSSのまとめと最終的な考え

これで、最新のトランジション効果を使用して非標準のスライダーレイアウトをコーディングする純粋なCSS / JSの方法がわかりました。

このアプローチが考え方として役立ち、必ずしも従来の設計ではなかったプロジェクトをコーディングするときに、フロントエンドプロジェクトで同様の何かを使用できることを願っています。

画像遷移効果に興味のある方のために、次の数行でこれについて説明します。

イントロセクションで提供したスライドのHTML構造をもう一度見ると、各画像スライドにdivがあることがわかります。 overflow:hiddenのCSSクラスでその周り。なにこれ&.prev { z-index: 5; transform: translate3d(-100%, 0, 0); transition: 1s $easing; .abs-mask { transform: translateX(80%); transition: 1s $easing; } } abs-maskを使用して、表示されている画像の一部を一定量非表示にします。画像とは異なる方向にオフセットします。たとえば、前のスライドのコーディング方法を見ると、次のようになります。

前のスライドのX軸は-100%オフセットされており、現在のスライドの左側に移動していますが、内側の

 divは80%右に変換され、より狭いビューポートを提供します。これは、アクティブなスライドのzインデックスを大きくすることと組み合わせて、一種のカバー効果をもたらします。アクティブな画像は前の画像をカバーすると同時に、全体像を提供するマスクを移動することで表示領域を拡張します。

基本を理解する

アニメーション化できるCSSプロパティとは何ですか?

アニメーション化できる最も標準的なCSSプロパティは、変換、不透明度、色、背景色、高さ、幅などです。完全なリストは、Mozillaの技術文書にあります。

CSSキーフレームアニメーションとは何ですか?

CSSキーフレームアニメーションは、指定された期間に選択された要素で発生する必要があるすべての遷移の0〜100%の時間表現です。このようにして、複数の遷移を組み合わせてシームレスな視覚的表現にすることができます。

遷移プロパティとは何ですか?

遷移は、CSSプロパティを2つの値の間で遷移できるようにするプロパティです。たとえば、要素にカーソルを合わせて、不透明度を0から1に遷移させます。

CSSの特異性とは何ですか?

特異性を解釈することにより、ブラウザはどのCSSルールを解釈するかを決定します。 CSSの特異性は、セレクターのタイプ(タイプセレクター、クラスセレクター、IDセレクター)によって異なります。複数のセレクターを組み合わせたり、兄弟と子のコンビネーターを追加したりすると、特異性も操作されます。

ブラウザのCSS:どのように機能しますか?

CSSでスタイル設定されたHTMLコンテンツを取得するには、ブラウザは次の方法を使用します。 HTMLをロードすると、そのコンテンツが提供されたスタイル情報と組み合わされ、DOMツリーが作成され、最後にそのコンテンツが表示されます。

Docker入門:DevOpsの簡素化

バックエンド

Docker入門:DevOpsの簡素化
Android開発のためのReactNativeに飛び込む

Android開発のためのReactNativeに飛び込む

モバイル

人気の投稿
AWSを使用した柔軟なA / Bテスト[メール保護]
AWSを使用した柔軟なA / Bテスト[メール保護]
なぜこれほど多くのPythonがあるのですか?
なぜこれほど多くのPythonがあるのですか?
Firebase認証を使用してロールベースのAPIを構築する方法
Firebase認証を使用してロールベースのAPIを構築する方法
適切に構造化されたロジック:GolangOOPチュートリアル
適切に構造化されたロジック:GolangOOPチュートリアル
ApeeScapeは、スタートアップ起業家に低コストでエリートプログラミングの才能を提供します
ApeeScapeは、スタートアップ起業家に低コストでエリートプログラミングの才能を提供します
 
スケッチとルーパーを使ってすぐに心を曲げるイラストを作成する
スケッチとルーパーを使ってすぐに心を曲げるイラストを作成する
ミニチュートリアル–Figmaボタンコンポーネントの操作
ミニチュートリアル–Figmaボタンコンポーネントの操作
Android開発者が犯す最も一般的な間違いトップ10:プログラミングチュートリアル
Android開発者が犯す最も一般的な間違いトップ10:プログラミングチュートリアル
最高のデータ視覚化ツールの完全な概要
最高のデータ視覚化ツールの完全な概要
将来のUIとデザインサンドボックスの終了
将来のUIとデザインサンドボックスの終了
人気の投稿
  • 次のうち、データベースの冗長性を回避するメリットではないものはどれですか?
  • 例を含むcssチートシート
  • ハッキングされたクレジットカード番号cvv
  • イーロンマスクに投資する方法
  • Twitterデータセットを取得する方法
  • 機械学習を定義し、いくつかの例を挙げます
カテゴリー
投資家と資金調達 製品の担当者とチーム 計画と予測 収益と成長 プロジェクト管理 分散チーム 仕事の未来 Uiデザイン 製品ライフサイクル モバイルデザイン

© 2021 | 全著作権所有

apeescape2.com