비디오 요소에 텍스트 또는 이미지 오버레이 추가

우리는 에서 영상 통화를 구축하는 많은 개발자들과 이야기를 나눴는데 그들이 자주 하고 싶어하는 한 가지는 비디오 요소 위에 텍스트(예: 참가자 이름) 또는 작은 이미지(음소거 상태 표시기 또는 로고)를 오버레이하는 것입니다. 이 게시물은 그 방법을 안내합니다!


내 이름을 추가하는 데 걸린 시간보다 한 손으로 이 스크린샷을 찍는 데 더 많은 시간이 걸렸습니다.

먼저 한 요소를 다른 요소 위에 배치하기 위한 기본 CSS를 다룰 것입니다. 그런 다음 해당 CSS를 적용하고 Paul의 .

CSS로 요소 배열 및 쌓기


positionz-index 속성을 설정하여 요소를 정렬합니다.

position 에서는 요소가 페이지의 전체 레이아웃에 배치되는 방식을 제어할 수 있습니다. 속성을 설정하지 않으면 모든 블록 수준 HTML 요소가 새 줄[0]에 나타납니다. 우리는 그것을 원하지 않습니다! 특히 이름 태그가 비디오 컨테이너 위에 직접 겹치기를 원합니다. 이름 태그의 위치는 동영상의 위치에 따라 다릅니다.

이 종속 관계를 설정하기 위해 비디오의 position 속성을 relative 로 설정합니다. 그런 다음 setting their position property to absolute 과 관련하여 모든 하위 요소(이 경우에는 이름 태그)를 정렬할 수 있습니다.

이를 확인하려면 이 코드펜의 position:relative 클래스에서 .parent-container를 제거하는 실험을 해보세요.



상자의 top , bottom , rightleft 속성은 .parent-container 에 대해 상쇄됩니다.

종속 관계가 설정되면 요소 쌓기로 넘어갈 차례입니다. 이를 위해서는 z-index 속성이 필요합니다. position 속성을 설정했기 때문에 z-index를 사용하여 요소를 쌓을 수 있습니다. z-index 숫자가 높을수록 요소가 화면에 더 가까워집니다. codepen에서 .red-box.green-box z-index 값을 바꿔 무슨 뜻인지 확인하십시오.

이제 position 를 사용하여 부모와 관련하여 자식 요소를 정렬하는 방법과 z-index 를 사용하여 쌓는 방법을 알고 있습니다. 이러한 개념을 에 적용할 준비가 되었지만 먼저 Daily call object 에서 참가자 이름을 가져오는 방법을 살펴보겠습니다.

React에서 참가자 이름을 소품으로 전달



The Daily call object은 회의에 대한 중요한 정보를 의미하는 통화 상태를 추적합니다. 여기에는 다른 참가자(예: 오디오 및 비디오 트랙 및 사용자_이름)와 같은 세부 정보와 통화 중 수행하는 작업(예: 마이크 음소거 또는 자리에서 나가기)[1]이 포함됩니다. 통화 개체는 회의와 상호 작용하기 위한 메서드도 제공합니다.

demo app 에서 Daily call 개체 상태를 callItems 의 해당 구성 요소 상태인 callState.js 에 매핑합니다. 각 통화 항목은 참가자를 나타내며 통화 로드 여부에 대한 부울 상태 표시기와 함께 오디오 및 비디오 트랙을 포함합니다. 참가자 이름도 추적하기 위해 각 호출 항목에 participantName를 추가합니다.

const initialCallState = {
 callItems: {
   local: {
     isLoading: true,
     audioTrack: null,
     videoTrack: null,
     participantName: '',
   },
 },
 clickAllowTimeoutFired: false,
 camOrMicError: null,
 fatalError: null,
};

participantName 함수에도 getCallItems를 추가해야 합니다. 이 함수는 호출 개체를 반복하여 callItems 를 채웁니다.

function getCallItems(participants, prevCallItems) {
 let callItems = { ...initialCallState.callItems }; // Ensure we *always* have a local participant
 for (const [id, participant] of Object.entries(participants)) {
   // Here we assume that a participant will join with audio/video enabled.
   // This assumption lets us show a "loading" state before we receive audio/video tracks.
   // This may not be true for all apps, but the call object doesn't yet support distinguishing
   // between cases where audio/video are missing because they're still loading or muted.
   const hasLoaded = prevCallItems[id] && !prevCallItems[id].isLoading;
   const missingTracks = !(participant.audioTrack || participant.videoTrack);
   callItems[id] = {
     isLoading: !hasLoaded && missingTracks,
     audioTrack: participant.audioTrack,
     videoTrack: participant.videoTrack,
     participantName: participant.user_name ? participant.user_name : 'Guest',
   };
   if (participant.screenVideoTrack || participant.screenAudioTrack) {
     callItems[id + '-screen'] = {
       isLoading: false,
       videoTrack: participant.screenVideoTrack,
       audioTrack: participant.screenAudioTrack,
     };
   }
 }
 return callItems;
}


getCallItems는 Call.js에서 호출됩니다[2]. 그런 다음 getTiles 함수를 통해 callItems를 각 참가자를 표시하는 구성 요소인 <Tile>에 전달합니다. 소품 목록에 participantName를 추가합니다.

export default function Call() {
// Lots of other things happen here! See our demo for full code.
//
function getTiles() {
   let largeTiles = [];
   let smallTiles = [];
   Object.entries(callState.callItems).forEach(([id, callItem]) => {
     const isLarge =
       isScreenShare(id) ||
       (!isLocal(id) && !containsScreenShare(callState.callItems));
     const tile = (
       <Tile
         key={id}
         videoTrack={callItem.videoTrack}
         audioTrack={callItem.audioTrack}
         isLocalPerson={isLocal(id)}
         isLarge={isLarge}
         isLoading={callItem.isLoading}
         participantName={callItem.participantName}
         onClick={
           isLocal(id)
             ? null
             : () => {
                 sendHello(id);
               }
         }
       />
     );
     if (isLarge) {
       largeTiles.push(tile);
     } else {
       smallTiles.push(tile);
     }
   });
   return [largeTiles, smallTiles];
 }

 const [largeTiles, smallTiles] = getTiles();

return (
   <div className="call">
     <div className="large-tiles">
       {
         !message
           ? largeTiles
           : null /* Avoid showing large tiles to make room for the message */
       }
     </div>
     <div className="small-tiles">{smallTiles}</div>
     {message && (
       <CallMessage
         header={message.header}
         detail={message.detail}
         isError={message.isError}
       />
     )}
   </div>
 );
}


이제 Tile.js에서 이름을 표시합니다.

export default function Tile(props) {
// More code
function getParticipantName() {
   return (
     props.participantName && (
       <div className="participant-name">{props.participantName}</div>
     )
   );
 }

 return (
   <div>
     <div className={getClassNames()} onClick={props.onClick}>
       <div className="background" />
       {getLoadingComponent()}
       {getVideoComponent()}
       {getAudioComponent()}
       {getParticipantName()}
     </div>
   </div>
 );
} 


그리고 Tile.css에서 친숙한 CSS를 사용하여 스타일을 지정합니다. 컨테이너 타일은 상대 위치로 설정되고 비디오 스트림과 이름 태그는 absolute로 설정됩니다.

.tile.small {
 width: 200px;
 margin: 0 10px;
 position: relative;
}

.tile.large {
 position: relative;
 margin: 2px;
}

.tile video {
 width: 100%;
 position: absolute;
 top: 0px;
 z-index: 1;
}

.participant-name {
 padding: 5px 5px;
 position: absolute;
 background: #ffffff;
 font-family: 'Helvetica Neue';
 font-style: normal;
 font-weight: normal;
 font-size: 1rem;
 line-height: 13px;
 text-align: center;
 color: #4a4a4a;
 top: 0;
 left: 0;
 z-index: 10;
}


그리고 당신은 그것을 가지고 있습니다!

이 게시물에 대한 질문이나 피드백이 있으면 언제든지 [email protected]으로 이메일을 보내주십시오. 또는 일일 통화를 사용자 정의하는 더 많은 방법을 찾고 있다면 explore our docs .

[0] inline elements 의 경우가 아닙니다.

[1] 참가자user_name는 몇 가지 다른 방법으로 설정할 수 있습니다. DailyIframe에 property 또는 set with a meeting token 으로 전달할 수 있습니다.

[2] 더 구체적으로 말하자면, 통화 참여자가 변경될 때마다 Call.js는 getCallItems를 통해 상태를 업데이트하는 리듀서에 작업을 발송합니다.

좋은 웹페이지 즐겨찾기